Merge pull request #315 from prometheus/refactor/client/new-model

Migrate Prometheus to Use Client-Side Model
This commit is contained in:
juliusv 2013-06-25 10:00:04 -07:00
commit 3cae867894
94 changed files with 1970 additions and 3762 deletions

View file

@ -17,8 +17,8 @@ include Makefile.INCLUDE
all: binary test all: binary test
$(GOCC): build/cache/$(GOPKG) $(GOCC): $(BUILD_PATH)/cache/$(GOPKG) source_path
tar -C build/root -xzf $< tar -C $(BUILD_PATH)/root -xzf $<
touch $@ touch $@
advice: advice:
@ -28,14 +28,14 @@ binary: build
build: config dependencies model preparation tools web build: config dependencies model preparation tools web
$(GO) build -o prometheus $(BUILDFLAGS) . $(GO) build -o prometheus $(BUILDFLAGS) .
cp prometheus build/package/prometheus cp prometheus $(BUILD_PATH)/package/prometheus
rsync -av build/root/lib/ build/package/lib/ rsync -av --delete $(BUILD_PATH)/root/lib/ $(BUILD_PATH)/package/lib/
build/cache/$(GOPKG): $(BUILD_PATH)/cache/$(GOPKG):
curl -o $@ http://go.googlecode.com/files/$(GOPKG) curl -o $@ http://go.googlecode.com/files/$(GOPKG)
clean: clean:
$(MAKE) -C build clean $(MAKE) -C $(BUILD_PATH) clean
$(MAKE) -C tools clean $(MAKE) -C tools clean
$(MAKE) -C web clean $(MAKE) -C web clean
rm -rf $(TEST_ARTIFACTS) rm -rf $(TEST_ARTIFACTS)
@ -53,16 +53,16 @@ documentation: search_index
godoc -http=:6060 -index -index_files='search_index' godoc -http=:6060 -index -index_files='search_index'
format: format:
find . -iname '*.go' | egrep -v "^./build/|generated|\.(l|y)\.go" | xargs -n1 $(GOFMT) -w -s=true find . -iname '*.go' | egrep -v "^\./\.build|./generated|\.(l|y)\.go" | xargs -n1 $(GOFMT) -w -s=true
model: dependencies preparation model: dependencies preparation
$(MAKE) -C model $(MAKE) -C model
preparation: $(GOCC) source_path preparation: $(GOCC) source_path
$(MAKE) -C build $(MAKE) -C $(BUILD_PATH)
race_condition_binary: build race_condition_binary: build
CGO_CFLAGS="-I$(PWD)/build/root/include" CGO_LDFLAGS="-L$(PWD)/build/root/lib" $(GO) build -race -o prometheus.race $(BUILDFLAGS) . CGO_CFLAGS="-I$(BUILD_PATH)/root/include" CGO_LDFLAGS="-L$(BUILD_PATH)/root/lib" $(GO) build -race -o prometheus.race $(BUILDFLAGS) .
race_condition_run: race_condition_binary race_condition_run: race_condition_binary
./prometheus.race $(ARGUMENTS) ./prometheus.race $(ARGUMENTS)
@ -83,13 +83,16 @@ source_path:
[ -d "$(FULL_GOPATH)" ] [ -d "$(FULL_GOPATH)" ]
test: build test: build
$(GOENV) find . -maxdepth 1 -mindepth 1 -type d -and -not -path ./build -exec $(GOCC) test {}/... $(GO_TEST_FLAGS) \; $(GOENV) find . -maxdepth 1 -mindepth 1 -type d -and -not -path $(BUILD_PATH) -exec $(GOCC) test {}/... $(GO_TEST_FLAGS) \;
$(GO) test $(GO_TEST_FLAGS) $(GO) test $(GO_TEST_FLAGS)
tools: dependencies preparation tools: dependencies preparation
$(MAKE) -C tools $(MAKE) -C tools
update:
$(GO) get -d
web: config dependencies model preparation web: config dependencies model preparation
$(MAKE) -C web $(MAKE) -C web
.PHONY: advice binary build clean config dependencies documentation format model package preparation race_condition_binary race_condition_run run search_index source_path test tools .PHONY: advice binary build clean config dependencies documentation format model package preparation race_condition_binary race_condition_run run search_index source_path test tools update

View file

@ -28,17 +28,19 @@ endif
OS=$(shell uname) OS=$(shell uname)
ARCH=$(shell uname -m) ARCH=$(shell uname -m)
BUILD_PATH = $(PWD)/.build
GO_VERSION := 1.1 GO_VERSION := 1.1
GOOS = $(subst Darwin,darwin,$(subst Linux,linux,$(OS))) GOOS = $(subst Darwin,darwin,$(subst Linux,linux,$(OS)))
GOARCH = $(subst x86_64,amd64,$(ARCH)) GOARCH = $(subst x86_64,amd64,$(ARCH))
GOPKG = go$(GO_VERSION).$(GOOS)-$(GOARCH).tar.gz GOPKG = go$(GO_VERSION).$(GOOS)-$(GOARCH).tar.gz
GOROOT = $(PWD)/build/root/go GOROOT = $(BUILD_PATH)/root/go
GOPATH = $(PWD)/build/root/gopath GOPATH = $(BUILD_PATH)/root/gopath
GOCC = $(GOROOT)/bin/go GOCC = $(GOROOT)/bin/go
TMPDIR = /tmp TMPDIR = /tmp
GOENV = TMPDIR=$(TMPDIR) GOROOT=$(GOROOT) GOPATH=$(GOPATH) GOENV = TMPDIR=$(TMPDIR) GOROOT=$(GOROOT) GOPATH=$(GOPATH)
GO = $(GOENV) $(GOCC) GO = $(GOENV) $(GOCC)
GOFMT = $(GOROOT)/bin/gofmt GOFMT = $(GOROOT)/bin/gofmt
LEVELDB_VERSION := 1.12.0 LEVELDB_VERSION := 1.12.0
PROTOCOL_BUFFERS_VERSION := 2.5.0 PROTOCOL_BUFFERS_VERSION := 2.5.0
@ -48,7 +50,7 @@ UNAME := $(shell uname)
FULL_GOPATH := $(GOPATH)/src/github.com/prometheus/prometheus FULL_GOPATH := $(GOPATH)/src/github.com/prometheus/prometheus
FULL_GOPATH_BASE := $(GOPATH)/src/github.com/prometheus FULL_GOPATH_BASE := $(GOPATH)/src/github.com/prometheus
export PREFIX=$(PWD)/build/root export PREFIX=$(BUILD_PATH)/root
export LOCAL_BINARIES=$(PREFIX)/bin export LOCAL_BINARIES=$(PREFIX)/bin

54
main.go
View file

@ -15,32 +15,31 @@ package main
import ( import (
"flag" "flag"
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/retrieval"
"github.com/prometheus/prometheus/retrieval/format"
"github.com/prometheus/prometheus/rules"
"github.com/prometheus/prometheus/storage/metric"
"github.com/prometheus/prometheus/storage/raw/leveldb"
"github.com/prometheus/prometheus/web"
"github.com/prometheus/prometheus/web/api"
"log" "log"
"os" "os"
"os/signal" "os/signal"
"sync" "sync"
"time" "time"
"github.com/prometheus/client_golang/extraction"
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/retrieval"
"github.com/prometheus/prometheus/rules"
"github.com/prometheus/prometheus/storage/metric"
"github.com/prometheus/prometheus/storage/raw/leveldb"
"github.com/prometheus/prometheus/web"
"github.com/prometheus/prometheus/web/api"
) )
const ( const deletionBatchSize = 100
deletionBatchSize = 100
)
// Commandline flags. // Commandline flags.
var ( var (
printVersion = flag.Bool("version", false, "print version information") printVersion = flag.Bool("version", false, "print version information")
configFile = flag.String("configFile", "prometheus.conf", "Prometheus configuration file name.") configFile = flag.String("configFile", "prometheus.conf", "Prometheus configuration file name.")
metricsStoragePath = flag.String("metricsStoragePath", "/tmp/metrics", "Base path for metrics storage.") metricsStoragePath = flag.String("metricsStoragePath", "/tmp/metrics", "Base path for metrics storage.")
scrapeResultsQueueCapacity = flag.Int("scrapeResultsQueueCapacity", 4096, "The size of the scrape results queue.") samplesQueueCapacity = flag.Int("samplesQueueCapacity", 4096, "The size of the unwritten samples queue.")
ruleResultsQueueCapacity = flag.Int("ruleResultsQueueCapacity", 4096, "The size of the rule results queue.")
concurrentRetrievalAllowance = flag.Int("concurrentRetrievalAllowance", 15, "The number of concurrent metrics retrieval requests allowed.") concurrentRetrievalAllowance = flag.Int("concurrentRetrievalAllowance", 15, "The number of concurrent metrics retrieval requests allowed.")
diskAppendQueueCapacity = flag.Int("queue.diskAppendCapacity", 1000000, "The size of the queue for items that are pending writing to disk.") diskAppendQueueCapacity = flag.Int("queue.diskAppendCapacity", 1000000, "The size of the queue for items that are pending writing to disk.")
memoryAppendQueueCapacity = flag.Int("queue.memoryAppendCapacity", 10000, "The size of the queue for items that are pending writing to memory.") memoryAppendQueueCapacity = flag.Int("queue.memoryAppendCapacity", 10000, "The size of the queue for items that are pending writing to memory.")
@ -76,8 +75,7 @@ type prometheus struct {
databaseStates chan []leveldb.DatabaseState databaseStates chan []leveldb.DatabaseState
stopBackgroundOperations chan bool stopBackgroundOperations chan bool
ruleResults chan *rules.Result unwrittenSamples chan *extraction.Result
scrapeResults chan format.Result
storage *metric.TieredStorage storage *metric.TieredStorage
} }
@ -198,8 +196,7 @@ func main() {
log.Fatalln("Nil tiered storage.") log.Fatalln("Nil tiered storage.")
} }
scrapeResults := make(chan format.Result, *scrapeResultsQueueCapacity) unwrittenSamples := make(chan *extraction.Result, *samplesQueueCapacity)
ruleResults := make(chan *rules.Result, *ruleResultsQueueCapacity)
curationState := make(chan metric.CurationState, 1) curationState := make(chan metric.CurationState, 1)
databaseStates := make(chan []leveldb.DatabaseState, 1) databaseStates := make(chan []leveldb.DatabaseState, 1)
// Coprime numbers, fool! // Coprime numbers, fool!
@ -209,11 +206,11 @@ func main() {
deletionTimer := time.NewTicker(*deleteInterval) deletionTimer := time.NewTicker(*deleteInterval)
// Queue depth will need to be exposed // Queue depth will need to be exposed
targetManager := retrieval.NewTargetManager(scrapeResults, *concurrentRetrievalAllowance) targetManager := retrieval.NewTargetManager(unwrittenSamples, *concurrentRetrievalAllowance)
targetManager.AddTargetsFromConfig(conf) targetManager.AddTargetsFromConfig(conf)
// Queue depth will need to be exposed // Queue depth will need to be exposed
ruleManager := rules.NewRuleManager(ruleResults, conf.EvaluationInterval(), ts) ruleManager := rules.NewRuleManager(unwrittenSamples, conf.EvaluationInterval(), ts)
err = ruleManager.AddRulesFromConfig(conf) err = ruleManager.AddRulesFromConfig(conf)
if err != nil { if err != nil {
log.Fatalf("Error loading rule files: %v", err) log.Fatalf("Error loading rule files: %v", err)
@ -259,7 +256,7 @@ func main() {
AlertsHandler: alertsHandler, AlertsHandler: alertsHandler,
} }
prometheus := prometheus{ prometheus := &prometheus{
bodyCompactionTimer: bodyCompactionTimer, bodyCompactionTimer: bodyCompactionTimer,
headCompactionTimer: headCompactionTimer, headCompactionTimer: headCompactionTimer,
tailCompactionTimer: tailCompactionTimer, tailCompactionTimer: tailCompactionTimer,
@ -271,8 +268,7 @@ func main() {
curationState: curationState, curationState: curationState,
databaseStates: databaseStates, databaseStates: databaseStates,
ruleResults: ruleResults, unwrittenSamples: unwrittenSamples,
scrapeResults: scrapeResults,
stopBackgroundOperations: make(chan bool, 1), stopBackgroundOperations: make(chan bool, 1),
@ -343,17 +339,9 @@ func main() {
}() }()
// TODO(all): Migrate this into prometheus.serve(). // TODO(all): Migrate this into prometheus.serve().
for { for block := range unwrittenSamples {
select { if block.Err == nil {
case scrapeResult := <-scrapeResults: ts.AppendSamples(block.Samples)
if scrapeResult.Err == nil {
ts.AppendSamples(scrapeResult.Samples)
}
case ruleResult := <-ruleResults:
if ruleResult.Err == nil {
ts.AppendSamples(ruleResult.Samples)
}
} }
} }
} }

View file

@ -1,103 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"bytes"
"code.google.com/p/goprotobuf/proto"
"fmt"
dto "github.com/prometheus/prometheus/model/generated"
"time"
)
// CurationRemark provides a representation of dto.CurationValue with associated
// business logic methods attached to it to enhance code readability.
type CurationRemark struct {
LastCompletionTimestamp time.Time
}
// OlderThan answers whether this CurationRemark is older than the provided
// cutOff time.
func (c CurationRemark) OlderThan(t time.Time) bool {
return c.LastCompletionTimestamp.Before(t)
}
// Equal answers whether the two CurationRemarks are equivalent.
func (c CurationRemark) Equal(o CurationRemark) bool {
return c.LastCompletionTimestamp.Equal(o.LastCompletionTimestamp)
}
func (c CurationRemark) String() string {
return fmt.Sprintf("Last curated at %s", c.LastCompletionTimestamp)
}
// ToDTO generates the dto.CurationValue representation of this.
func (c CurationRemark) ToDTO() *dto.CurationValue {
return &dto.CurationValue{
LastCompletionTimestamp: proto.Int64(c.LastCompletionTimestamp.Unix()),
}
}
// NewCurationRemarkFromDTO builds CurationRemark from the provided
// dto.CurationValue object.
func NewCurationRemarkFromDTO(d *dto.CurationValue) CurationRemark {
return CurationRemark{
LastCompletionTimestamp: time.Unix(*d.LastCompletionTimestamp, 0).UTC(),
}
}
// CurationKey provides a representation of dto.CurationKey with associated
// business logic methods attached to it to enhance code readability.
type CurationKey struct {
Fingerprint *Fingerprint
ProcessorMessageRaw []byte
ProcessorMessageTypeName string
IgnoreYoungerThan time.Duration
}
// Equal answers whether the two CurationKeys are equivalent.
func (c CurationKey) Equal(o CurationKey) bool {
switch {
case !c.Fingerprint.Equal(o.Fingerprint):
return false
case bytes.Compare(c.ProcessorMessageRaw, o.ProcessorMessageRaw) != 0:
return false
case c.ProcessorMessageTypeName != o.ProcessorMessageTypeName:
return false
case c.IgnoreYoungerThan != o.IgnoreYoungerThan:
return false
}
return true
}
// ToDTO generates a dto.CurationKey representation of this.
func (c CurationKey) ToDTO() *dto.CurationKey {
return &dto.CurationKey{
Fingerprint: c.Fingerprint.ToDTO(),
ProcessorMessageRaw: c.ProcessorMessageRaw,
ProcessorMessageTypeName: proto.String(c.ProcessorMessageTypeName),
IgnoreYoungerThan: proto.Int64(int64(c.IgnoreYoungerThan)),
}
}
// NewCurationKeyFromDTO builds CurationKey from the provided dto.CurationKey.
func NewCurationKeyFromDTO(d *dto.CurationKey) CurationKey {
return CurationKey{
Fingerprint: NewFingerprintFromDTO(d.Fingerprint),
ProcessorMessageRaw: d.ProcessorMessageRaw,
ProcessorMessageTypeName: *d.ProcessorMessageTypeName,
IgnoreYoungerThan: time.Duration(*d.IgnoreYoungerThan),
}
}

View file

@ -1,131 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"code.google.com/p/goprotobuf/proto"
dto "github.com/prometheus/prometheus/model/generated"
"sort"
"time"
)
func SampleToMetricDTO(s *Sample) *dto.Metric {
labelLength := len(s.Metric)
labelNames := make([]string, 0, labelLength)
for labelName := range s.Metric {
labelNames = append(labelNames, string(labelName))
}
sort.Strings(labelNames)
labelSets := make([]*dto.LabelPair, 0, labelLength)
for _, labelName := range labelNames {
labelValue := s.Metric[LabelName(labelName)]
labelPair := &dto.LabelPair{
Name: proto.String(string(labelName)),
Value: proto.String(string(labelValue)),
}
labelSets = append(labelSets, labelPair)
}
return &dto.Metric{
LabelPair: labelSets,
}
}
func MetricToDTO(m Metric) *dto.Metric {
metricLength := len(m)
labelNames := make([]string, 0, metricLength)
for labelName := range m {
labelNames = append(labelNames, string(labelName))
}
sort.Strings(labelNames)
labelSets := make([]*dto.LabelPair, 0, metricLength)
for _, labelName := range labelNames {
l := LabelName(labelName)
labelValue := m[l]
labelPair := &dto.LabelPair{
Name: proto.String(string(labelName)),
Value: proto.String(string(labelValue)),
}
labelSets = append(labelSets, labelPair)
}
return &dto.Metric{
LabelPair: labelSets,
}
}
func LabelSetToDTOs(s *LabelSet) []*dto.LabelPair {
metricLength := len(*s)
labelNames := make([]string, 0, metricLength)
for labelName := range *s {
labelNames = append(labelNames, string(labelName))
}
sort.Strings(labelNames)
labelSets := make([]*dto.LabelPair, 0, metricLength)
for _, labelName := range labelNames {
l := LabelName(labelName)
labelValue := (*s)[l]
labelPair := &dto.LabelPair{
Name: proto.String(string(labelName)),
Value: proto.String(string(labelValue)),
}
labelSets = append(labelSets, labelPair)
}
return labelSets
}
func LabelSetToDTO(s *LabelSet) *dto.LabelSet {
return &dto.LabelSet{
Member: LabelSetToDTOs(s),
}
}
func LabelNameToDTO(l *LabelName) *dto.LabelName {
return &dto.LabelName{
Name: proto.String(string(*l)),
}
}
func FingerprintToDTO(f *Fingerprint) *dto.Fingerprint {
return &dto.Fingerprint{
Signature: proto.String(f.ToRowKey()),
}
}
func SampleFromDTO(m *Metric, t *time.Time, v *dto.SampleValueSeries) *Sample {
s := &Sample{
Value: SampleValue(*v.Value[0].Value),
Timestamp: *t,
}
s.Metric = *m
return s
}

View file

@ -1,207 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"code.google.com/p/goprotobuf/proto"
"encoding/binary"
"fmt"
dto "github.com/prometheus/prometheus/model/generated"
"hash/fnv"
"sort"
"strconv"
"strings"
)
const (
// rowKeyDelimiter is used to separate formatted versions of a metric's row
// key.
rowKeyDelimiter = "-"
)
// Builds a Fingerprint from a row key.
func NewFingerprintFromRowKey(rowKey string) *Fingerprint {
components := strings.Split(rowKey, rowKeyDelimiter)
hash, err := strconv.ParseUint(components[0], 10, 64)
if err != nil {
panic(err)
}
labelMatterLength, err := strconv.ParseUint(components[2], 10, 0)
if err != nil {
panic(err)
}
return &Fingerprint{
hash: hash,
firstCharacterOfFirstLabelName: components[1],
labelMatterLength: uint(labelMatterLength),
lastCharacterOfLastLabelValue: components[3],
}
}
// Builds a Fingerprint from a datastore entry.
func NewFingerprintFromDTO(f *dto.Fingerprint) *Fingerprint {
return NewFingerprintFromRowKey(*f.Signature)
}
// Decomposes a Metric into a Fingerprint.
func NewFingerprintFromMetric(metric Metric) *Fingerprint {
labelLength := len(metric)
labelNames := make([]string, 0, labelLength)
for labelName := range metric {
labelNames = append(labelNames, string(labelName))
}
sort.Strings(labelNames)
summer := fnv.New64a()
firstCharacterOfFirstLabelName := ""
lastCharacterOfLastLabelValue := ""
labelMatterLength := 0
for i, labelName := range labelNames {
labelValue := metric[LabelName(labelName)]
labelNameLength := len(labelName)
labelValueLength := len(labelValue)
labelMatterLength += labelNameLength + labelValueLength
if i == 0 {
firstCharacterOfFirstLabelName = labelName[0:1]
}
if i == labelLength-1 {
lastCharacterOfLastLabelValue = string(labelValue[labelValueLength-1 : labelValueLength])
}
summer.Write([]byte(labelName))
summer.Write([]byte(reservedDelimiter))
summer.Write([]byte(labelValue))
}
return &Fingerprint{
firstCharacterOfFirstLabelName: firstCharacterOfFirstLabelName,
hash: binary.LittleEndian.Uint64(summer.Sum(nil)),
labelMatterLength: uint(labelMatterLength % 10),
lastCharacterOfLastLabelValue: lastCharacterOfLastLabelValue,
}
}
// A simplified representation of an entity.
type Fingerprint struct {
// A hashed representation of the underyling entity. For our purposes, FNV-1A
// 64-bit is used.
hash uint64
firstCharacterOfFirstLabelName string
labelMatterLength uint
lastCharacterOfLastLabelValue string
}
func (f *Fingerprint) String() string {
return f.ToRowKey()
}
// Transforms the Fingerprint into a database row key.
func (f *Fingerprint) ToRowKey() string {
return strings.Join([]string{fmt.Sprintf("%020d", f.hash), f.firstCharacterOfFirstLabelName, fmt.Sprint(f.labelMatterLength), f.lastCharacterOfLastLabelValue}, rowKeyDelimiter)
}
func (f *Fingerprint) ToDTO() *dto.Fingerprint {
return &dto.Fingerprint{
Signature: proto.String(f.ToRowKey()),
}
}
func (f *Fingerprint) Hash() uint64 {
return f.hash
}
func (f *Fingerprint) FirstCharacterOfFirstLabelName() string {
return f.firstCharacterOfFirstLabelName
}
func (f *Fingerprint) LabelMatterLength() uint {
return f.labelMatterLength
}
func (f *Fingerprint) LastCharacterOfLastLabelValue() string {
return f.lastCharacterOfLastLabelValue
}
func (f *Fingerprint) Less(o *Fingerprint) bool {
if f.hash < o.hash {
return true
}
if f.hash > o.hash {
return false
}
if f.firstCharacterOfFirstLabelName < o.firstCharacterOfFirstLabelName {
return true
}
if f.firstCharacterOfFirstLabelName > o.firstCharacterOfFirstLabelName {
return false
}
if f.labelMatterLength < o.labelMatterLength {
return true
}
if f.labelMatterLength > o.labelMatterLength {
return false
}
if f.lastCharacterOfLastLabelValue < o.lastCharacterOfLastLabelValue {
return true
}
if f.lastCharacterOfLastLabelValue > o.lastCharacterOfLastLabelValue {
return false
}
return false
}
func (f *Fingerprint) Equal(o *Fingerprint) (equal bool) {
equal = f.Hash() == o.Hash()
if !equal {
return
}
equal = f.FirstCharacterOfFirstLabelName() == o.FirstCharacterOfFirstLabelName()
if !equal {
return
}
equal = f.LabelMatterLength() == o.LabelMatterLength()
if !equal {
return
}
equal = f.LastCharacterOfLastLabelValue() == o.LastCharacterOfLastLabelValue()
return
}
// Represents a collection of Fingerprint subject to a given natural sorting
// scheme.
type Fingerprints []*Fingerprint
func (f Fingerprints) Len() int {
return len(f)
}
func (f Fingerprints) Less(i, j int) bool {
return f[i].Less(f[j])
}
func (f Fingerprints) Swap(i, j int) {
f[i], f[j] = f[j], f[i]
}

View file

@ -1,104 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"runtime"
"testing"
)
func TestFingerprintComparison(t *testing.T) {
fingerprints := []*Fingerprint{
{
hash: 0,
firstCharacterOfFirstLabelName: "b",
labelMatterLength: 1,
lastCharacterOfLastLabelValue: "b",
},
{
hash: 1,
firstCharacterOfFirstLabelName: "a",
labelMatterLength: 0,
lastCharacterOfLastLabelValue: "a",
},
{
hash: 1,
firstCharacterOfFirstLabelName: "a",
labelMatterLength: 1000,
lastCharacterOfLastLabelValue: "b",
},
{
hash: 1,
firstCharacterOfFirstLabelName: "b",
labelMatterLength: 0,
lastCharacterOfLastLabelValue: "a",
},
{
hash: 1,
firstCharacterOfFirstLabelName: "b",
labelMatterLength: 1,
lastCharacterOfLastLabelValue: "a",
},
{
hash: 1,
firstCharacterOfFirstLabelName: "b",
labelMatterLength: 1,
lastCharacterOfLastLabelValue: "b",
},
}
for i := range fingerprints {
if i == 0 {
continue
}
if !fingerprints[i-1].Less(fingerprints[i]) {
t.Errorf("%d expected %s < %s", i, fingerprints[i-1], fingerprints[i])
}
}
}
func BenchmarkFingerprinting(b *testing.B) {
b.StopTimer()
fps := []*Fingerprint{
{
hash: 0,
firstCharacterOfFirstLabelName: "a",
labelMatterLength: 2,
lastCharacterOfLastLabelValue: "z",
},
{
hash: 0,
firstCharacterOfFirstLabelName: "a",
labelMatterLength: 2,
lastCharacterOfLastLabelValue: "z",
},
}
for i := 0; i < 10; i++ {
fps[0].Less(fps[1])
}
b.Logf("N: %v", b.N)
b.StartTimer()
var pre runtime.MemStats
runtime.ReadMemStats(&pre)
for i := 0; i < b.N; i++ {
fps[0].Less(fps[1])
}
var post runtime.MemStats
runtime.ReadMemStats(&post)
b.Logf("allocs: %d items: ", post.TotalAlloc-pre.TotalAlloc)
}

View file

@ -1,64 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"strings"
)
const (
// The label name indicating the metric name of a timeseries.
MetricNameLabel = LabelName("name")
// The label name indicating the job from which a timeseries was scraped.
JobLabel = LabelName("job")
// The label name indicating the instance from which a timeseries was scraped.
InstanceLabel = LabelName("instance")
// The label name prefix to prepend if a synthetic label is already present
// in the exported metrics.
ExporterLabelPrefix = LabelName("exporter_")
// The metric name for the synthetic health variable.
ScrapeHealthMetricName = LabelValue("up")
// The metric name for synthetic alert timeseries.
AlertMetricName = LabelValue("ALERTS")
// The label name indicating the name of an alert.
AlertNameLabel = LabelName("alertname")
// The label name indicating the state of an alert.
AlertStateLabel = LabelName("alertstate")
)
// A LabelName is a key for a LabelSet or Metric. It has a value associated
// therewith.
type LabelName string
type LabelNames []LabelName
func (l LabelNames) Len() int {
return len(l)
}
func (l LabelNames) Less(i, j int) bool {
return l[i] < l[j]
}
func (l LabelNames) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
func (l LabelNames) String() string {
labelStrings := make([]string, 0, len(l))
for _, label := range l {
labelStrings = append(labelStrings, string(label))
}
return strings.Join(labelStrings, ", ")
}

View file

@ -1,56 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"github.com/prometheus/prometheus/utility/test"
"sort"
"testing"
)
func testLabelNames(t test.Tester) {
var scenarios = []struct {
in LabelNames
out LabelNames
}{
{
in: LabelNames{"ZZZ", "zzz"},
out: LabelNames{"ZZZ", "zzz"},
},
{
in: LabelNames{"aaa", "AAA"},
out: LabelNames{"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 TestLabelNames(t *testing.T) {
testLabelNames(t)
}
func BenchmarkLabelNames(b *testing.B) {
for i := 0; i < b.N; i++ {
testLabelNames(b)
}
}

View file

@ -1,35 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"sort"
)
// A LabelValue is an associated value for a LabelName.
type LabelValue string
type LabelValues []LabelValue
func (l LabelValues) Len() int {
return len(l)
}
func (l LabelValues) Less(i, j int) bool {
return sort.StringsAreSorted([]string{string(l[i]), string(l[j])})
}
func (l LabelValues) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}

View file

@ -1,56 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"github.com/prometheus/prometheus/utility/test"
"sort"
"testing"
)
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 TestLabelValues(t *testing.T) {
testLabelValues(t)
}
func BenchmarkLabelValues(b *testing.B) {
for i := 0; i < b.N; i++ {
testLabelValues(b)
}
}

View file

@ -1,262 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"bytes"
"code.google.com/p/goprotobuf/proto"
"fmt"
dto "github.com/prometheus/prometheus/model/generated"
"sort"
"strings"
"time"
)
const (
// XXX: Re-evaluate down the road.
reservedDelimiter = `"`
)
// A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet
// may be fully-qualified down to the point where it may resolve to a single
// Metric in the data store or not. All operations that occur within the realm
// of a LabelSet can emit a vector of Metric entities to which the LabelSet may
// match.
type LabelSet map[LabelName]LabelValue
// Helper function to non-destructively merge two label sets.
func (l LabelSet) Merge(other LabelSet) LabelSet {
result := make(LabelSet, len(l))
for k, v := range l {
result[k] = v
}
for k, v := range other {
result[k] = v
}
return result
}
func (l LabelSet) String() string {
labelStrings := make([]string, 0, len(l))
for label, value := range l {
labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
}
switch len(labelStrings) {
case 0:
return ""
default:
sort.Strings(labelStrings)
return fmt.Sprintf("{%s}", strings.Join(labelStrings, ", "))
}
}
func (m Metric) String() string {
metricName, ok := m[MetricNameLabel]
if !ok {
panic("Tried to print metric without name")
}
labelStrings := make([]string, 0, len(m)-1)
for label, value := range m {
if label != MetricNameLabel {
labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
}
}
switch len(labelStrings) {
case 0:
return string(metricName)
default:
sort.Strings(labelStrings)
return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ", "))
}
}
func (l LabelSet) ToMetric() Metric {
metric := Metric{}
for label, value := range l {
metric[label] = value
}
return metric
}
func (m Metric) ToLabelSet() LabelSet {
labels := LabelSet{}
for label, value := range m {
labels[label] = value
}
return labels
}
// A Metric is similar to a LabelSet, but the key difference is that a Metric is
// a singleton and refers to one and only one stream of samples.
type Metric map[LabelName]LabelValue
// A SampleValue is a representation of a value for a given sample at a given
// time.
type SampleValue float64
func (s SampleValue) Equal(o SampleValue) bool {
return s == o
}
func (s SampleValue) ToDTO() *float64 {
return proto.Float64(float64(s))
}
func (v SampleValue) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"%f"`, v)), nil
}
func (v SampleValue) String() string {
return fmt.Sprint(float64(v))
}
func (s SamplePair) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("{\"Value\": \"%f\", \"Timestamp\": %d}", s.Value, s.Timestamp.Unix())), nil
}
type SamplePair struct {
Value SampleValue
Timestamp time.Time
}
func (s SamplePair) Equal(o SamplePair) bool {
return s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp)
}
func (s SamplePair) ToDTO() (out *dto.SampleValueSeries_Value) {
out = &dto.SampleValueSeries_Value{
Timestamp: proto.Int64(s.Timestamp.Unix()),
Value: s.Value.ToDTO(),
}
return
}
func (s SamplePair) String() string {
return fmt.Sprintf("SamplePair at %s of %s", s.Timestamp, s.Value)
}
type Values []SamplePair
func (v Values) Len() int {
return len(v)
}
func (v Values) Less(i, j int) bool {
return v[i].Timestamp.Before(v[j].Timestamp)
}
func (v Values) Swap(i, j int) {
v[i], v[j] = v[j], v[i]
}
// FirstTimeAfter indicates whether the first sample of a set is after a given
// timestamp.
func (v Values) FirstTimeAfter(t time.Time) bool {
return v[0].Timestamp.After(t)
}
// LastTimeBefore indicates whether the last sample of a set is before a given
// timestamp.
func (v Values) LastTimeBefore(t time.Time) bool {
return v[len(v)-1].Timestamp.Before(t)
}
// InsideInterval indicates whether a given range of sorted values could contain
// a value for a given time.
func (v Values) InsideInterval(t time.Time) bool {
switch {
case v.Len() == 0:
return false
case t.Before(v[0].Timestamp):
return false
case !v[v.Len()-1].Timestamp.Before(t):
return false
default:
return true
}
}
// TruncateBefore returns a subslice of the original such that extraneous
// samples in the collection that occur before the provided time are
// dropped. The original slice is not mutated
func (v Values) TruncateBefore(t time.Time) Values {
index := sort.Search(len(v), func(i int) bool {
timestamp := v[i].Timestamp
return !timestamp.Before(t)
})
return v[index:]
}
func (v Values) ToDTO() (out *dto.SampleValueSeries) {
out = &dto.SampleValueSeries{}
for _, value := range v {
out.Value = append(out.Value, value.ToDTO())
}
return
}
func (v Values) ToSampleKey(f *Fingerprint) SampleKey {
return SampleKey{
Fingerprint: f,
FirstTimestamp: v[0].Timestamp,
LastTimestamp: v[len(v)-1].Timestamp,
SampleCount: uint32(len(v)),
}
}
func (v Values) String() string {
buffer := bytes.Buffer{}
fmt.Fprintf(&buffer, "[")
for i, value := range v {
fmt.Fprintf(&buffer, "%d. %s", i, value)
if i != len(v)-1 {
fmt.Fprintf(&buffer, "\n")
}
}
fmt.Fprintf(&buffer, "]")
return buffer.String()
}
func NewValuesFromDTO(dto *dto.SampleValueSeries) (v Values) {
for _, value := range dto.Value {
v = append(v, SamplePair{
Timestamp: time.Unix(*value.Timestamp, 0).UTC(),
Value: SampleValue(*value.Value),
})
}
return v
}
type SampleSet struct {
Metric Metric
Values Values
}
type Interval struct {
OldestInclusive time.Time
NewestInclusive time.Time
}

View file

@ -1,229 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"github.com/prometheus/prometheus/utility/test"
"testing"
"time"
)
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-6-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 TestMetric(t *testing.T) {
testMetric(t)
}
func BenchmarkMetric(b *testing.B) {
for i := 0; i < b.N; i++ {
testMetric(b)
}
}
func testTruncateBefore(t test.Tester) {
type in struct {
values Values
time time.Time
}
instant := time.Now()
var scenarios = []struct {
in in
out Values
}{
{
in: in{
time: instant,
values: Values{
{
Value: 0,
Timestamp: instant,
},
{
Value: 1,
Timestamp: instant.Add(time.Second),
},
{
Value: 2,
Timestamp: instant.Add(2 * time.Second),
},
{
Value: 3,
Timestamp: instant.Add(3 * time.Second),
},
{
Value: 4,
Timestamp: instant.Add(4 * time.Second),
},
},
},
out: Values{
{
Value: 0,
Timestamp: instant,
},
{
Value: 1,
Timestamp: instant.Add(time.Second),
},
{
Value: 2,
Timestamp: instant.Add(2 * time.Second),
},
{
Value: 3,
Timestamp: instant.Add(3 * time.Second),
},
{
Value: 4,
Timestamp: instant.Add(4 * time.Second),
},
},
},
{
in: in{
time: instant.Add(2 * time.Second),
values: Values{
{
Value: 0,
Timestamp: instant,
},
{
Value: 1,
Timestamp: instant.Add(time.Second),
},
{
Value: 2,
Timestamp: instant.Add(2 * time.Second),
},
{
Value: 3,
Timestamp: instant.Add(3 * time.Second),
},
{
Value: 4,
Timestamp: instant.Add(4 * time.Second),
},
},
},
out: Values{
{
Value: 2,
Timestamp: instant.Add(2 * time.Second),
},
{
Value: 3,
Timestamp: instant.Add(3 * time.Second),
},
{
Value: 4,
Timestamp: instant.Add(4 * time.Second),
},
},
},
{
in: in{
time: instant.Add(5 * time.Second),
values: Values{
{
Value: 0,
Timestamp: instant,
},
{
Value: 1,
Timestamp: instant.Add(time.Second),
},
{
Value: 2,
Timestamp: instant.Add(2 * time.Second),
},
{
Value: 3,
Timestamp: instant.Add(3 * time.Second),
},
{
Value: 4,
Timestamp: instant.Add(4 * time.Second),
},
},
},
out: Values{},
},
}
for i, scenario := range scenarios {
actual := scenario.in.values.TruncateBefore(scenario.in.time)
if len(actual) != len(scenario.out) {
t.Fatalf("%d. expected length of %d, got %d", i, len(scenario.out), len(actual))
}
for j, actualValue := range actual {
if !actualValue.Equal(scenario.out[j]) {
t.Fatalf("%d.%d. expected %s, got %s", i, j, scenario.out[j], actualValue)
}
}
}
}
func TestTruncateBefore(t *testing.T) {
testTruncateBefore(t)
}

View file

@ -1,59 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"time"
)
type Sample struct {
Metric Metric
Value SampleValue
Timestamp time.Time
}
func (s Sample) Equal(sample Sample) bool {
if !NewFingerprintFromMetric(s.Metric).Equal(NewFingerprintFromMetric(sample.Metric)) {
return false
}
if !s.Timestamp.Equal(sample.Timestamp) {
return false
}
if !s.Value.Equal(sample.Value) {
return false
}
return true
}
type Samples []Sample
func (s Samples) Len() int {
return len(s)
}
func (s Samples) Less(i, j int) bool {
switch {
case NewFingerprintFromMetric(s[i].Metric).Less(NewFingerprintFromMetric(s[j].Metric)):
return true
case s[i].Timestamp.Before(s[j].Timestamp):
return true
default:
return false
}
}
func (s Samples) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

View file

@ -1,49 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"code.google.com/p/goprotobuf/proto"
dto "github.com/prometheus/prometheus/model/generated"
"time"
)
// Watermark provides a representation of dto.MetricHighWatermark with
// associated business logic methods attached to it to enhance code readability.
type Watermark struct {
time.Time
}
// ToMetricHighWatermarkDTO builds a MetricHighWatermark DTO out of a given
// Watermark.
func (w Watermark) ToMetricHighWatermarkDTO() *dto.MetricHighWatermark {
return &dto.MetricHighWatermark{
Timestamp: proto.Int64(w.Time.Unix()),
}
}
// NewWatermarkFromHighWatermarkDTO builds Watermark from the provided
// dto.MetricHighWatermark object.
func NewWatermarkFromHighWatermarkDTO(d *dto.MetricHighWatermark) Watermark {
return Watermark{
time.Unix(*d.Timestamp, 0).UTC(),
}
}
// NewWatermarkFromTime builds a new Watermark for the provided time.
func NewWatermarkFromTime(t time.Time) Watermark {
return Watermark{
t,
}
}

View file

@ -1,68 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package format
import (
"fmt"
"mime"
"net/http"
)
var (
DefaultRegistry Registry = &registry{}
)
// Registry is responsible for applying a determination strategy to the given
// inputs to determine what Processor can handle this type of input.
type Registry interface {
// ProcessorForRequestHeader interprets a HTTP request header to determine
// what Processor should be used for the given input.
ProcessorForRequestHeader(header http.Header) (Processor, error)
}
type registry struct {
}
func (r *registry) ProcessorForRequestHeader(header http.Header) (Processor, error) {
if header == nil {
return nil, fmt.Errorf("Received illegal and nil header.")
}
mediatype, params, err := mime.ParseMediaType(header.Get("Content-Type"))
if err != nil {
return nil, fmt.Errorf("Invalid Content-Type header %q: %s", header.Get("Content-Type"), err)
}
if mediatype != "application/json" {
return nil, fmt.Errorf("Unsupported media type %q, expected %q", mediatype, "application/json")
}
var prometheusApiVersion string
if params["schema"] == "prometheus/telemetry" && params["version"] != "" {
prometheusApiVersion = params["version"]
} else {
prometheusApiVersion = header.Get("X-Prometheus-API-Version")
}
switch prometheusApiVersion {
case "0.0.2":
return Processor002, nil
case "0.0.1":
return Processor001, nil
default:
return nil, fmt.Errorf("Unrecognized API version %s", prometheusApiVersion)
}
}

View file

@ -1,92 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package format
import (
"fmt"
"github.com/prometheus/prometheus/utility/test"
"net/http"
"testing"
)
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{"Content-Type": "application/json", "X-Prometheus-API-Version": "0.0.0"},
output: nil,
err: fmt.Errorf("Unrecognized API version 0.0.0"),
},
{
input: map[string]string{"Content-Type": "application/json", "X-Prometheus-API-Version": "0.0.1"},
output: Processor001,
err: nil,
},
{
input: map[string]string{"Content-Type": `application/json; schema="prometheus/telemetry"; version=0.0.0`},
output: nil,
err: fmt.Errorf("Unrecognized API version 0.0.0"),
},
{
input: map[string]string{"Content-Type": `application/json; schema="prometheus/telemetry"; 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 TestDiscriminatorHttpHeader(t *testing.T) {
testDiscriminatorHttpHeader(t)
}
func BenchmarkDiscriminatorHttpHeader(b *testing.B) {
for i := 0; i < b.N; i++ {
testDiscriminatorHttpHeader(b)
}
}

View file

@ -1,79 +0,0 @@
[
{
"baseLabels": {
"name": "rpc_calls_total",
"job": "batch_job"
},
"docstring": "RPC calls.",
"metric": {
"type": "counter",
"value": [
{
"labels": {
"service": "zed"
},
"value": 25
},
{
"labels": {
"service": "bar"
},
"value": 25
},
{
"labels": {
"service": "foo"
},
"value": 25
}
]
}
},
{
"baseLabels": {
"name": "rpc_latency_microseconds"
},
"docstring": "RPC latency.",
"metric": {
"type": "histogram",
"value": [
{
"labels": {
"service": "foo"
},
"value": {
"0.010000": 15.890724674774395,
"0.050000": 15.890724674774395,
"0.500000": 84.63044031436561,
"0.900000": 160.21100853053224,
"0.990000": 172.49828748957728
}
},
{
"labels": {
"service": "zed"
},
"value": {
"0.010000": 0.0459814091918713,
"0.050000": 0.0459814091918713,
"0.500000": 0.6120456642749681,
"0.900000": 1.355915069887731,
"0.990000": 1.772733213161236
}
},
{
"labels": {
"service": "bar"
},
"value": {
"0.010000": 78.48563317257356,
"0.050000": 78.48563317257356,
"0.500000": 97.31798360385088,
"0.900000": 109.89202084295582,
"0.990000": 109.99626121011262
}
}
]
}
}
]

View file

@ -1,22 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package format
import (
"testing"
)
func TestInterface(t *testing.T) {
var _ Registry = &registry{}
}

View file

@ -1,70 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package format
import (
"github.com/prometheus/prometheus/model"
"io"
"time"
)
// Processor is responsible for decoding the actual message responses from
// stream into a format that can be consumed with the end result written
// to the results channel.
type Processor interface {
// Process performs the work on the input and closes the incoming stream.
Process(stream io.ReadCloser, timestamp time.Time, baseLabels model.LabelSet, results chan Result) (err error)
}
// The ProcessorFunc type allows the use of ordinary functions for processors.
type ProcessorFunc func(io.ReadCloser, time.Time, model.LabelSet, chan Result) error
func (f ProcessorFunc) Process(stream io.ReadCloser, timestamp time.Time, baseLabels model.LabelSet, results chan Result) error {
return f(stream, timestamp, baseLabels, results)
}
// Helper function to convert map[string]string into model.LabelSet.
//
// NOTE: This should be deleted when support for go 1.0.3 is removed; 1.1 is
// smart enough to unmarshal JSON objects into model.LabelSet directly.
func LabelSet(labels map[string]string) model.LabelSet {
labelset := make(model.LabelSet, len(labels))
for k, v := range labels {
labelset[model.LabelName(k)] = model.LabelValue(v)
}
return labelset
}
// Helper function to merge a target's base labels ontop of the labels of an
// exported sample. If a label is already defined in the exported sample, we
// assume that we are scraping an intermediate exporter and attach
// "exporter_"-prefixes to Prometheus' own base labels.
func mergeTargetLabels(entityLabels, targetLabels model.LabelSet) model.LabelSet {
result := model.LabelSet{}
for label, value := range entityLabels {
result[label] = value
}
for label, labelValue := range targetLabels {
if _, exists := result[label]; exists {
result[model.ExporterLabelPrefix+label] = labelValue
} else {
result[label] = labelValue
}
}
return result
}

View file

@ -1,140 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package format
import (
"encoding/json"
"fmt"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/utility"
"io"
"io/ioutil"
"time"
)
const (
baseLabels001 = "baseLabels"
counter001 = "counter"
docstring001 = "docstring"
gauge001 = "gauge"
histogram001 = "histogram"
labels001 = "labels"
metric001 = "metric"
type001 = "type"
value001 = "value"
percentile001 = "percentile"
)
var (
Processor001 Processor = &processor001{}
)
// processor001 is responsible for handling API version 0.0.1.
type processor001 struct {
time utility.Time
}
// entity001 represents a the JSON structure that 0.0.1 uses.
type entity001 []struct {
BaseLabels map[string]string `json:"baseLabels"`
Docstring string `json:"docstring"`
Metric struct {
MetricType string `json:"type"`
Value []struct {
Labels map[string]string `json:"labels"`
Value interface{} `json:"value"`
} `json:"value"`
} `json:"metric"`
}
func (p *processor001) Process(stream io.ReadCloser, timestamp time.Time, baseLabels model.LabelSet, results chan Result) error {
// TODO(matt): Replace with plain-jane JSON unmarshalling.
defer stream.Close()
buffer, err := ioutil.ReadAll(stream)
if err != nil {
return err
}
entities := entity001{}
if err = json.Unmarshal(buffer, &entities); err != nil {
return err
}
// TODO(matt): This outer loop is a great basis for parallelization.
pendingSamples := model.Samples{}
for _, entity := range entities {
for _, value := range entity.Metric.Value {
entityLabels := LabelSet(entity.BaseLabels).Merge(LabelSet(value.Labels))
labels := mergeTargetLabels(entityLabels, baseLabels)
switch entity.Metric.MetricType {
case gauge001, counter001:
sampleValue, ok := value.Value.(float64)
if !ok {
err = fmt.Errorf("Could not convert value from %s %s to float64.", entity, value)
results <- Result{Err: err}
continue
}
pendingSamples = append(pendingSamples, model.Sample{
Metric: model.Metric(labels),
Timestamp: timestamp,
Value: model.SampleValue(sampleValue),
})
break
case histogram001:
sampleValue, ok := value.Value.(map[string]interface{})
if !ok {
err = fmt.Errorf("Could not convert value from %q to a map[string]interface{}.", value.Value)
results <- Result{Err: err}
continue
}
for percentile, percentileValue := range sampleValue {
individualValue, ok := percentileValue.(float64)
if !ok {
err = fmt.Errorf("Could not convert value from %q to a float64.", percentileValue)
results <- Result{Err: err}
continue
}
childMetric := make(map[model.LabelName]model.LabelValue, len(labels)+1)
for k, v := range labels {
childMetric[k] = v
}
childMetric[model.LabelName(percentile001)] = model.LabelValue(percentile)
pendingSamples = append(pendingSamples, model.Sample{
Metric: model.Metric(childMetric),
Timestamp: timestamp,
Value: model.SampleValue(individualValue),
})
}
break
}
}
}
if len(pendingSamples) > 0 {
results <- Result{Samples: pendingSamples}
}
return nil
}

View file

@ -1,216 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package format
import (
"container/list"
"fmt"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/utility/test"
"os"
"path"
"testing"
"time"
)
func testProcessor001Process(t test.Tester) {
var scenarios = []struct {
in string
baseLabels model.LabelSet
out model.Samples
err error
}{
{
in: "empty.json",
err: fmt.Errorf("unexpected end of JSON input"),
},
{
in: "test0_0_1-0_0_2.json",
baseLabels: model.LabelSet{
model.JobLabel: "batch_exporter",
},
out: model.Samples{
model.Sample{
Metric: model.Metric{"service": "zed", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job", "exporter_job": "batch_exporter"},
Value: 25,
},
model.Sample{
Metric: model.Metric{"service": "bar", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job", "exporter_job": "batch_exporter"},
Value: 25,
},
model.Sample{
Metric: model.Metric{"service": "foo", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job", "exporter_job": "batch_exporter"},
Value: 25,
},
model.Sample{
Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed", "job": "batch_exporter"},
Value: 0.0459814091918713,
},
model.Sample{
Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar", "job": "batch_exporter"},
Value: 78.48563317257356,
},
model.Sample{
Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo", "job": "batch_exporter"},
Value: 15.890724674774395,
},
model.Sample{
Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed", "job": "batch_exporter"},
Value: 0.0459814091918713,
},
model.Sample{
Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar", "job": "batch_exporter"},
Value: 78.48563317257356,
},
model.Sample{
Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo", "job": "batch_exporter"},
Value: 15.890724674774395,
},
model.Sample{
Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed", "job": "batch_exporter"},
Value: 0.6120456642749681,
},
model.Sample{
Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar", "job": "batch_exporter"},
Value: 97.31798360385088,
},
model.Sample{
Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo", "job": "batch_exporter"},
Value: 84.63044031436561,
},
model.Sample{
Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed", "job": "batch_exporter"},
Value: 1.355915069887731,
},
model.Sample{
Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar", "job": "batch_exporter"},
Value: 109.89202084295582,
},
model.Sample{
Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo", "job": "batch_exporter"},
Value: 160.21100853053224,
},
model.Sample{
Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed", "job": "batch_exporter"},
Value: 1.772733213161236,
},
model.Sample{
Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar", "job": "batch_exporter"},
Value: 109.99626121011262,
},
model.Sample{
Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo", "job": "batch_exporter"},
Value: 172.49828748957728,
},
},
},
}
for i, scenario := range scenarios {
inputChannel := make(chan Result, 1024)
defer func(c chan Result) {
close(c)
}(inputChannel)
reader, err := os.Open(path.Join("fixtures", scenario.in))
if err != nil {
t.Fatalf("%d. couldn't open scenario input file %s: %s", i, scenario.in, err)
}
err = Processor001.Process(reader, time.Now(), scenario.baseLabels, inputChannel)
if !test.ErrorEqual(scenario.err, err) {
t.Errorf("%d. expected err of %s, got %s", i, scenario.err, err)
continue
}
delivered := model.Samples{}
for len(inputChannel) != 0 {
result := <-inputChannel
if result.Err != nil {
t.Fatalf("%d. expected no error, got: %s", i, result.Err)
}
delivered = append(delivered, result.Samples...)
}
if len(delivered) != len(scenario.out) {
t.Errorf("%d. expected output length of %d, got %d", i, len(scenario.out), len(delivered))
continue
}
expectedElements := list.New()
for _, j := range scenario.out {
expectedElements.PushBack(j)
}
for j := 0; j < len(delivered); j++ {
actual := delivered[j]
found := false
for element := expectedElements.Front(); element != nil && found == false; element = element.Next() {
candidate := element.Value.(model.Sample)
if candidate.Value != actual.Value {
continue
}
if len(candidate.Metric) != len(actual.Metric) {
continue
}
labelsMatch := false
for key, value := range candidate.Metric {
actualValue, ok := actual.Metric[key]
if !ok {
break
}
if actualValue == value {
labelsMatch = true
break
}
}
if !labelsMatch {
continue
}
// XXX: Test time.
found = true
expectedElements.Remove(element)
}
if !found {
t.Errorf("%d.%d. expected to find %s among candidate, absent", i, j, actual)
}
}
}
}
func TestProcessor001Process(t *testing.T) {
testProcessor001Process(t)
}
func BenchmarkProcessor001Process(b *testing.B) {
for i := 0; i < b.N; i++ {
testProcessor001Process(b)
}
}

View file

@ -1,114 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package format
import (
"encoding/json"
"fmt"
"github.com/prometheus/prometheus/model"
"io"
"time"
)
// Processor for telemetry schema version 0.0.2.
var Processor002 ProcessorFunc = func(stream io.ReadCloser, timestamp time.Time, baseLabels model.LabelSet, results chan Result) error {
// container for telemetry data
var entities []struct {
BaseLabels map[string]string `json:"baseLabels"`
Docstring string `json:"docstring"`
Metric struct {
Type string `json:"type"`
Values json.RawMessage `json:"value"`
} `json:"metric"`
}
// concrete type for histogram values
type histogram struct {
Labels map[string]string `json:"labels"`
Values map[string]model.SampleValue `json:"value"`
}
// concrete type for counter and gauge values
type counter struct {
Labels map[string]string `json:"labels"`
Value model.SampleValue `json:"value"`
}
defer stream.Close()
if err := json.NewDecoder(stream).Decode(&entities); err != nil {
return err
}
pendingSamples := model.Samples{}
for _, entity := range entities {
switch entity.Metric.Type {
case "counter", "gauge":
var values []counter
if err := json.Unmarshal(entity.Metric.Values, &values); err != nil {
results <- Result{
Err: fmt.Errorf("Could not extract %s value: %s", entity.Metric.Type, err),
}
continue
}
for _, counter := range values {
entityLabels := LabelSet(entity.BaseLabels).Merge(LabelSet(counter.Labels))
labels := mergeTargetLabels(entityLabels, baseLabels)
pendingSamples = append(pendingSamples, model.Sample{
Metric: model.Metric(labels),
Timestamp: timestamp,
Value: counter.Value,
})
}
case "histogram":
var values []histogram
if err := json.Unmarshal(entity.Metric.Values, &values); err != nil {
results <- Result{
Err: fmt.Errorf("Could not extract %s value: %s", entity.Metric.Type, err),
}
continue
}
for _, histogram := range values {
for percentile, value := range histogram.Values {
entityLabels := LabelSet(entity.BaseLabels).Merge(LabelSet(histogram.Labels))
entityLabels[model.LabelName("percentile")] = model.LabelValue(percentile)
labels := mergeTargetLabels(entityLabels, baseLabels)
pendingSamples = append(pendingSamples, model.Sample{
Metric: model.Metric(labels),
Timestamp: timestamp,
Value: value,
})
}
}
default:
results <- Result{
Err: fmt.Errorf("Unknown metric type %q", entity.Metric.Type),
}
}
}
if len(pendingSamples) > 0 {
results <- Result{Samples: pendingSamples}
}
return nil
}

View file

@ -1,216 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package format
import (
"container/list"
"fmt"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/utility/test"
"os"
"path"
"testing"
"time"
)
func testProcessor002Process(t test.Tester) {
var scenarios = []struct {
in string
baseLabels model.LabelSet
out model.Samples
err error
}{
{
in: "empty.json",
err: fmt.Errorf("EOF"),
},
{
in: "test0_0_1-0_0_2.json",
baseLabels: model.LabelSet{
model.JobLabel: "batch_exporter",
},
out: model.Samples{
model.Sample{
Metric: model.Metric{"service": "zed", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job", "exporter_job": "batch_exporter"},
Value: 25,
},
model.Sample{
Metric: model.Metric{"service": "bar", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job", "exporter_job": "batch_exporter"},
Value: 25,
},
model.Sample{
Metric: model.Metric{"service": "foo", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job", "exporter_job": "batch_exporter"},
Value: 25,
},
model.Sample{
Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed", "job": "batch_exporter"},
Value: 0.0459814091918713,
},
model.Sample{
Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar", "job": "batch_exporter"},
Value: 78.48563317257356,
},
model.Sample{
Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo", "job": "batch_exporter"},
Value: 15.890724674774395,
},
model.Sample{
Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed", "job": "batch_exporter"},
Value: 0.0459814091918713,
},
model.Sample{
Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar", "job": "batch_exporter"},
Value: 78.48563317257356,
},
model.Sample{
Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo", "job": "batch_exporter"},
Value: 15.890724674774395,
},
model.Sample{
Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed", "job": "batch_exporter"},
Value: 0.6120456642749681,
},
model.Sample{
Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar", "job": "batch_exporter"},
Value: 97.31798360385088,
},
model.Sample{
Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo", "job": "batch_exporter"},
Value: 84.63044031436561,
},
model.Sample{
Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed", "job": "batch_exporter"},
Value: 1.355915069887731,
},
model.Sample{
Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar", "job": "batch_exporter"},
Value: 109.89202084295582,
},
model.Sample{
Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo", "job": "batch_exporter"},
Value: 160.21100853053224,
},
model.Sample{
Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed", "job": "batch_exporter"},
Value: 1.772733213161236,
},
model.Sample{
Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar", "job": "batch_exporter"},
Value: 109.99626121011262,
},
model.Sample{
Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo", "job": "batch_exporter"},
Value: 172.49828748957728,
},
},
},
}
for i, scenario := range scenarios {
inputChannel := make(chan Result, 1024)
defer func(c chan Result) {
close(c)
}(inputChannel)
reader, err := os.Open(path.Join("fixtures", scenario.in))
if err != nil {
t.Fatalf("%d. couldn't open scenario input file %s: %s", i, scenario.in, err)
}
err = Processor002.Process(reader, time.Now(), scenario.baseLabels, inputChannel)
if !test.ErrorEqual(scenario.err, err) {
t.Errorf("%d. expected err of %s, got %s", i, scenario.err, err)
continue
}
delivered := model.Samples{}
for len(inputChannel) != 0 {
result := <-inputChannel
if result.Err != nil {
t.Fatalf("%d. expected no error, got: %s", i, result.Err)
}
delivered = append(delivered, result.Samples...)
}
if len(delivered) != len(scenario.out) {
t.Errorf("%d. expected output length of %d, got %d", i, len(scenario.out), len(delivered))
continue
}
expectedElements := list.New()
for _, j := range scenario.out {
expectedElements.PushBack(j)
}
for j := 0; j < len(delivered); j++ {
actual := delivered[j]
found := false
for element := expectedElements.Front(); element != nil && found == false; element = element.Next() {
candidate := element.Value.(model.Sample)
if candidate.Value != actual.Value {
continue
}
if len(candidate.Metric) != len(actual.Metric) {
continue
}
labelsMatch := false
for key, value := range candidate.Metric {
actualValue, ok := actual.Metric[key]
if !ok {
break
}
if actualValue == value {
labelsMatch = true
break
}
}
if !labelsMatch {
continue
}
// XXX: Test time.
found = true
expectedElements.Remove(element)
}
if !found {
t.Errorf("%d.%d. expected to find %s among candidate, absent", i, j, actual)
}
}
}
}
func TestProcessor002Process(t *testing.T) {
testProcessor002Process(t)
}
func BenchmarkProcessor002Process(b *testing.B) {
for i := 0; i < b.N; i++ {
testProcessor002Process(b)
}
}

View file

@ -1,24 +0,0 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package format
import (
"github.com/prometheus/prometheus/model"
)
// Result encapsulates the outcome from processing samples from a source.
type Result struct {
Err error
Samples model.Samples
}

View file

@ -14,19 +14,26 @@ package retrieval
import ( import (
"fmt" "fmt"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/retrieval/format"
"log" "log"
"net/http" "net/http"
"os" "os"
"strings" "strings"
"time" "time"
"github.com/prometheus/client_golang/extraction"
clientmodel "github.com/prometheus/client_golang/model"
) )
var ( const (
localhostRepresentations = []string{"http://127.0.0.1", "http://localhost"} InstanceLabel clientmodel.LabelName = "instance"
// The metric name for the synthetic health variable.
ScrapeHealthMetricName clientmodel.LabelValue = "up"
) )
var localhostRepresentations = []string{"http://127.0.0.1", "http://localhost"}
// The state of the given Target. // The state of the given Target.
type TargetState int type TargetState int
@ -81,7 +88,7 @@ type Target interface {
// alluded to in the scheduledFor function, to use this as it wants to. The // alluded to in the scheduledFor function, to use this as it wants to. The
// current use case is to create a common batching time for scraping multiple // current use case is to create a common batching time for scraping multiple
// Targets in the future through the TargetPool. // Targets in the future through the TargetPool.
Scrape(earliest time.Time, results chan format.Result) error Scrape(earliest time.Time, results chan<- *extraction.Result) error
// Fulfill the healthReporter interface. // Fulfill the healthReporter interface.
State() TargetState State() TargetState
// Report the soonest time at which this Target may be scheduled for // Report the soonest time at which this Target may be scheduled for
@ -100,7 +107,7 @@ type Target interface {
// to the address of the prometheus server. // to the address of the prometheus server.
GlobalAddress() string GlobalAddress() string
// Return the target's base labels. // Return the target's base labels.
BaseLabels() model.LabelSet BaseLabels() clientmodel.LabelSet
// Merge a new externally supplied target definition (e.g. with changed base // Merge a new externally supplied target definition (e.g. with changed base
// labels) into an old target definition for the same endpoint. Preserve // labels) into an old target definition for the same endpoint. Preserve
// remaining information - like health state - from the old target. // remaining information - like health state - from the old target.
@ -121,13 +128,13 @@ type target struct {
// What is the deadline for the HTTP or HTTPS against this endpoint. // What is the deadline for the HTTP or HTTPS against this endpoint.
Deadline time.Duration Deadline time.Duration
// Any base labels that are added to this target and its metrics. // Any base labels that are added to this target and its metrics.
baseLabels model.LabelSet baseLabels clientmodel.LabelSet
// The HTTP client used to scrape the target's endpoint. // The HTTP client used to scrape the target's endpoint.
client http.Client client http.Client
} }
// Furnish a reasonably configured target for querying. // Furnish a reasonably configured target for querying.
func NewTarget(address string, deadline time.Duration, baseLabels model.LabelSet) Target { func NewTarget(address string, deadline time.Duration, baseLabels clientmodel.LabelSet) Target {
target := &target{ target := &target{
address: address, address: address,
Deadline: deadline, Deadline: deadline,
@ -143,32 +150,32 @@ func NewTarget(address string, deadline time.Duration, baseLabels model.LabelSet
return target return target
} }
func (t *target) recordScrapeHealth(results chan format.Result, timestamp time.Time, healthy bool) { func (t *target) recordScrapeHealth(results chan<- *extraction.Result, timestamp time.Time, healthy bool) {
metric := model.Metric{} metric := clientmodel.Metric{}
for label, value := range t.baseLabels { for label, value := range t.baseLabels {
metric[label] = value metric[label] = value
} }
metric[model.MetricNameLabel] = model.ScrapeHealthMetricName metric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(ScrapeHealthMetricName)
metric[model.InstanceLabel] = model.LabelValue(t.Address()) metric[InstanceLabel] = clientmodel.LabelValue(t.Address())
healthValue := model.SampleValue(0) healthValue := clientmodel.SampleValue(0)
if healthy { if healthy {
healthValue = model.SampleValue(1) healthValue = clientmodel.SampleValue(1)
} }
sample := model.Sample{ sample := &clientmodel.Sample{
Metric: metric, Metric: metric,
Timestamp: timestamp, Timestamp: timestamp,
Value: healthValue, Value: healthValue,
} }
results <- format.Result{ results <- &extraction.Result{
Err: nil, Err: nil,
Samples: model.Samples{sample}, Samples: clientmodel.Samples{sample},
} }
} }
func (t *target) Scrape(earliest time.Time, results chan format.Result) (err error) { func (t *target) Scrape(earliest time.Time, results chan<- *extraction.Result) (err error) {
now := time.Now() now := time.Now()
futureState := t.state futureState := t.state
@ -187,7 +194,7 @@ func (t *target) Scrape(earliest time.Time, results chan format.Result) (err err
return err return err
} }
func (t *target) scrape(timestamp time.Time, results chan format.Result) (err error) { func (t *target) scrape(timestamp time.Time, results chan<- *extraction.Result) (err error) {
defer func(start time.Time) { defer func(start time.Time) {
ms := float64(time.Since(start)) / float64(time.Millisecond) ms := float64(time.Since(start)) / float64(time.Millisecond)
labels := map[string]string{address: t.Address(), outcome: success} labels := map[string]string{address: t.Address(), outcome: success}
@ -205,19 +212,24 @@ func (t *target) scrape(timestamp time.Time, results chan format.Result) (err er
} }
defer resp.Body.Close() defer resp.Body.Close()
processor, err := format.DefaultRegistry.ProcessorForRequestHeader(resp.Header) processor, err := extraction.ProcessorForRequestHeader(resp.Header)
if err != nil { if err != nil {
return err return err
} }
// XXX: This is a wart; we need to handle this more gracefully down the // XXX: This is a wart; we need to handle this more gracefully down the
// road, especially once we have service discovery support. // road, especially once we have service discovery support.
baseLabels := model.LabelSet{model.InstanceLabel: model.LabelValue(t.Address())} baseLabels := clientmodel.LabelSet{InstanceLabel: clientmodel.LabelValue(t.Address())}
for baseLabel, baseValue := range t.baseLabels { for baseLabel, baseValue := range t.baseLabels {
baseLabels[baseLabel] = baseValue baseLabels[baseLabel] = baseValue
} }
return processor.Process(resp.Body, timestamp, baseLabels, results) processOptions := &extraction.ProcessOptions{
Timestamp: timestamp,
BaseLabels: baseLabels,
}
return processor.ProcessSingle(resp.Body, results, processOptions)
} }
func (t target) State() TargetState { func (t target) State() TargetState {
@ -249,7 +261,7 @@ func (t target) GlobalAddress() string {
return address return address
} }
func (t target) BaseLabels() model.LabelSet { func (t target) BaseLabels() clientmodel.LabelSet {
return t.baseLabels return t.baseLabels
} }

View file

@ -19,8 +19,9 @@ import (
"net/url" "net/url"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/utility" "github.com/prometheus/prometheus/utility"
) )
@ -61,8 +62,8 @@ func (p *sdTargetProvider) Targets() ([]Target, error) {
return nil, err return nil, err
} }
baseLabels := model.LabelSet{ baseLabels := clientmodel.LabelSet{
model.JobLabel: model.LabelValue(p.job.GetName()), clientmodel.JobLabel: clientmodel.LabelValue(p.job.GetName()),
} }
targets := make([]Target, 0, len(addrs)) targets := make([]Target, 0, len(addrs))

View file

@ -14,12 +14,14 @@
package retrieval package retrieval
import ( import (
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/retrieval/format"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/client_golang/extraction"
) )
func TestTargetScrapeUpdatesState(t *testing.T) { func TestTargetScrapeUpdatesState(t *testing.T) {
@ -28,7 +30,7 @@ func TestTargetScrapeUpdatesState(t *testing.T) {
state: UNKNOWN, state: UNKNOWN,
address: "bad schema", address: "bad schema",
} }
testTarget.Scrape(time.Time{}, make(chan format.Result, 2)) testTarget.Scrape(time.Time{}, make(chan *extraction.Result, 2))
if testTarget.state != UNREACHABLE { if testTarget.state != UNREACHABLE {
t.Errorf("Expected target state %v, actual: %v", UNREACHABLE, testTarget.state) t.Errorf("Expected target state %v, actual: %v", UNREACHABLE, testTarget.state)
} }
@ -38,11 +40,11 @@ func TestTargetRecordScrapeHealth(t *testing.T) {
testTarget := target{ testTarget := target{
scheduler: literalScheduler{}, scheduler: literalScheduler{},
address: "http://example.url", address: "http://example.url",
baseLabels: model.LabelSet{model.JobLabel: "testjob"}, baseLabels: clientmodel.LabelSet{clientmodel.JobLabel: "testjob"},
} }
now := time.Now() now := time.Now()
results := make(chan format.Result) results := make(chan *extraction.Result)
go testTarget.recordScrapeHealth(results, now, true) go testTarget.recordScrapeHealth(results, now, true)
result := <-results result := <-results
@ -52,11 +54,11 @@ func TestTargetRecordScrapeHealth(t *testing.T) {
} }
actual := result.Samples[0] actual := result.Samples[0]
expected := model.Sample{ expected := &clientmodel.Sample{
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: model.ScrapeHealthMetricName, clientmodel.MetricNameLabel: ScrapeHealthMetricName,
model.InstanceLabel: "http://example.url", InstanceLabel: "http://example.url",
model.JobLabel: "testjob", clientmodel.JobLabel: "testjob",
}, },
Timestamp: now, Timestamp: now,
Value: 1, Value: 1,
@ -81,8 +83,8 @@ func TestTargetScrapeTimeout(t *testing.T) {
defer server.Close() defer server.Close()
testTarget := NewTarget(server.URL, 10*time.Millisecond, model.LabelSet{}) testTarget := NewTarget(server.URL, 10*time.Millisecond, clientmodel.LabelSet{})
results := make(chan format.Result, 1024) results := make(chan *extraction.Result, 1024)
// scrape once without timeout // scrape once without timeout
signal <- true signal <- true

View file

@ -14,11 +14,13 @@
package retrieval package retrieval
import ( import (
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/retrieval/format"
"log" "log"
"time" "time"
"github.com/prometheus/client_golang/extraction"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/config"
) )
type TargetManager interface { type TargetManager interface {
@ -34,10 +36,10 @@ type TargetManager interface {
type targetManager struct { type targetManager struct {
requestAllowance chan bool requestAllowance chan bool
poolsByJob map[string]*TargetPool poolsByJob map[string]*TargetPool
results chan format.Result results chan<- *extraction.Result
} }
func NewTargetManager(results chan format.Result, requestAllowance int) TargetManager { func NewTargetManager(results chan<- *extraction.Result, requestAllowance int) TargetManager {
return &targetManager{ return &targetManager{
requestAllowance: make(chan bool, requestAllowance), requestAllowance: make(chan bool, requestAllowance),
results: results, results: results,
@ -97,12 +99,12 @@ func (m *targetManager) AddTargetsFromConfig(config config.Config) {
} }
for _, targetGroup := range job.TargetGroup { for _, targetGroup := range job.TargetGroup {
baseLabels := model.LabelSet{ baseLabels := clientmodel.LabelSet{
model.JobLabel: model.LabelValue(job.GetName()), clientmodel.JobLabel: clientmodel.LabelValue(job.GetName()),
} }
if targetGroup.Labels != nil { if targetGroup.Labels != nil {
for _, label := range targetGroup.Labels.Label { for _, label := range targetGroup.Labels.Label {
baseLabels[model.LabelName(label.GetName())] = model.LabelValue(label.GetValue()) baseLabels[clientmodel.LabelName(label.GetName())] = clientmodel.LabelValue(label.GetValue())
} }
} }

View file

@ -14,14 +14,19 @@
package retrieval package retrieval
import ( import (
"code.google.com/p/goprotobuf/proto"
"github.com/prometheus/prometheus/config"
pb "github.com/prometheus/prometheus/config/generated"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/retrieval/format"
"github.com/prometheus/prometheus/utility/test"
"testing" "testing"
"time" "time"
"code.google.com/p/goprotobuf/proto"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/client_golang/extraction"
pb "github.com/prometheus/prometheus/config/generated"
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/utility/test"
) )
type fakeTarget struct { type fakeTarget struct {
@ -43,15 +48,15 @@ func (t fakeTarget) GlobalAddress() string {
return t.Address() return t.Address()
} }
func (t fakeTarget) BaseLabels() model.LabelSet { func (t fakeTarget) BaseLabels() clientmodel.LabelSet {
return model.LabelSet{} return clientmodel.LabelSet{}
} }
func (t fakeTarget) Interval() time.Duration { func (t fakeTarget) Interval() time.Duration {
return t.interval return t.interval
} }
func (t *fakeTarget) Scrape(e time.Time, r chan format.Result) error { func (t *fakeTarget) Scrape(e time.Time, r chan<- *extraction.Result) error {
t.scrapeCount++ t.scrapeCount++
return nil return nil
@ -71,7 +76,7 @@ func (t *fakeTarget) scheduledFor() (time time.Time) {
func (t *fakeTarget) Merge(newTarget Target) {} func (t *fakeTarget) Merge(newTarget Target) {}
func testTargetManager(t test.Tester) { func testTargetManager(t test.Tester) {
results := make(chan format.Result, 5) results := make(chan *extraction.Result, 5)
targetManager := NewTargetManager(results, 3) targetManager := NewTargetManager(results, 3)
testJob1 := config.JobConfig{ testJob1 := config.JobConfig{
JobConfig: pb.JobConfig{ JobConfig: pb.JobConfig{

View file

@ -19,7 +19,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/prometheus/prometheus/retrieval/format" "github.com/prometheus/client_golang/extraction"
) )
const ( const (
@ -50,7 +50,7 @@ func NewTargetPool(m TargetManager, p TargetProvider) *TargetPool {
} }
} }
func (p *TargetPool) Run(results chan format.Result, interval time.Duration) { func (p *TargetPool) Run(results chan<- *extraction.Result, interval time.Duration) {
ticker := time.NewTicker(interval) ticker := time.NewTicker(interval)
defer ticker.Stop() defer ticker.Stop()
@ -116,14 +116,14 @@ func (p *TargetPool) replaceTargets(newTargets []Target) {
p.targets = newTargets p.targets = newTargets
} }
func (p *TargetPool) runSingle(earliest time.Time, results chan format.Result, t Target) { func (p *TargetPool) runSingle(earliest time.Time, results chan<- *extraction.Result, t Target) {
p.manager.acquire() p.manager.acquire()
defer p.manager.release() defer p.manager.release()
t.Scrape(earliest, results) t.Scrape(earliest, results)
} }
func (p *TargetPool) runIteration(results chan format.Result, interval time.Duration) { func (p *TargetPool) runIteration(results chan<- *extraction.Result, interval time.Duration) {
if p.targetProvider != nil { if p.targetProvider != nil {
targets, err := p.targetProvider.Targets() targets, err := p.targetProvider.Targets()
if err != nil { if err != nil {

View file

@ -18,7 +18,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/prometheus/prometheus/retrieval/format" "github.com/prometheus/client_golang/extraction"
"github.com/prometheus/prometheus/utility/test" "github.com/prometheus/prometheus/utility/test"
) )
@ -149,7 +150,7 @@ func TestTargetPoolIterationWithUnhealthyTargetsFinishes(t *testing.T) {
done := make(chan bool) done := make(chan bool)
go func() { go func() {
pool.runIteration(make(chan format.Result), time.Duration(0)) pool.runIteration(make(chan *extraction.Result), time.Duration(0))
done <- true done <- true
}() }()

View file

@ -19,13 +19,24 @@ import (
"sync" "sync"
"time" "time"
"github.com/prometheus/prometheus/model" clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/rules/ast" "github.com/prometheus/prometheus/rules/ast"
"github.com/prometheus/prometheus/stats" "github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/storage/metric"
"github.com/prometheus/prometheus/utility" "github.com/prometheus/prometheus/utility"
) )
const (
// The metric name for synthetic alert timeseries.
AlertMetricName clientmodel.LabelValue = "ALERTS"
// The label name indicating the name of an alert.
AlertNameLabel clientmodel.LabelName = "alertname"
// The label name indicating the state of an alert.
AlertStateLabel clientmodel.LabelName = "alertstate"
)
// States that active alerts can be in. // States that active alerts can be in.
type AlertState int type AlertState int
@ -53,27 +64,27 @@ type Alert struct {
// The name of the alert. // The name of the alert.
Name string Name string
// The vector element labelset triggering this alert. // The vector element labelset triggering this alert.
Labels model.LabelSet Labels clientmodel.LabelSet
// The state of the alert (PENDING or FIRING). // The state of the alert (PENDING or FIRING).
State AlertState State AlertState
// The time when the alert first transitioned into PENDING state. // The time when the alert first transitioned into PENDING state.
ActiveSince time.Time ActiveSince time.Time
// The value of the alert expression for this vector element. // The value of the alert expression for this vector element.
Value model.SampleValue Value clientmodel.SampleValue
} }
// sample returns a Sample suitable for recording the alert. // sample returns a Sample suitable for recording the alert.
func (a Alert) sample(timestamp time.Time, value model.SampleValue) model.Sample { func (a Alert) sample(timestamp time.Time, value clientmodel.SampleValue) *clientmodel.Sample {
recordedMetric := model.Metric{} recordedMetric := clientmodel.Metric{}
for label, value := range a.Labels { for label, value := range a.Labels {
recordedMetric[label] = value recordedMetric[label] = value
} }
recordedMetric[model.MetricNameLabel] = model.AlertMetricName recordedMetric[clientmodel.MetricNameLabel] = AlertMetricName
recordedMetric[model.AlertNameLabel] = model.LabelValue(a.Name) recordedMetric[AlertNameLabel] = clientmodel.LabelValue(a.Name)
recordedMetric[model.AlertStateLabel] = model.LabelValue(a.State.String()) recordedMetric[AlertStateLabel] = clientmodel.LabelValue(a.State.String())
return model.Sample{ return &clientmodel.Sample{
Metric: recordedMetric, Metric: recordedMetric,
Value: value, Value: value,
Timestamp: timestamp, Timestamp: timestamp,
@ -90,13 +101,13 @@ type AlertingRule struct {
// output vector before an alert transitions from PENDING to FIRING state. // output vector before an alert transitions from PENDING to FIRING state.
holdDuration time.Duration holdDuration time.Duration
// Extra labels to attach to the resulting alert sample vectors. // Extra labels to attach to the resulting alert sample vectors.
labels model.LabelSet labels clientmodel.LabelSet
// Protects the below. // Protects the below.
mutex sync.Mutex mutex sync.Mutex
// A map of alerts which are currently active (PENDING or FIRING), keyed by // A map of alerts which are currently active (PENDING or FIRING), keyed by
// the fingerprint of the labelset they correspond to. // the fingerprint of the labelset they correspond to.
activeAlerts map[model.Fingerprint]*Alert activeAlerts map[clientmodel.Fingerprint]*Alert
} }
func (rule *AlertingRule) Name() string { return rule.name } func (rule *AlertingRule) Name() string { return rule.name }
@ -119,16 +130,17 @@ func (rule *AlertingRule) Eval(timestamp time.Time, storage *metric.TieredStorag
// or update the expression value for existing elements. // or update the expression value for existing elements.
resultFingerprints := utility.Set{} resultFingerprints := utility.Set{}
for _, sample := range exprResult { for _, sample := range exprResult {
fp := *model.NewFingerprintFromMetric(sample.Metric) fp := new(clientmodel.Fingerprint)
resultFingerprints.Add(fp) fp.LoadFromMetric(sample.Metric)
resultFingerprints.Add(*fp)
alert, ok := rule.activeAlerts[fp] if alert, ok := rule.activeAlerts[*fp]; !ok {
if !ok { labels := clientmodel.LabelSet{}
labels := sample.Metric.ToLabelSet() labels.MergeFromMetric(sample.Metric)
if _, ok := labels[model.MetricNameLabel]; ok { if _, ok := labels[clientmodel.MetricNameLabel]; ok {
delete(labels, model.MetricNameLabel) delete(labels, clientmodel.MetricNameLabel)
} }
rule.activeAlerts[fp] = &Alert{ rule.activeAlerts[*fp] = &Alert{
Name: rule.name, Name: rule.name,
Labels: labels, Labels: labels,
State: PENDING, State: PENDING,
@ -175,9 +187,9 @@ func (rule *AlertingRule) String() string {
} }
func (rule *AlertingRule) HTMLSnippet() template.HTML { func (rule *AlertingRule) HTMLSnippet() template.HTML {
alertMetric := model.Metric{ alertMetric := clientmodel.Metric{
model.MetricNameLabel: model.AlertMetricName, clientmodel.MetricNameLabel: AlertMetricName,
model.AlertNameLabel: model.LabelValue(rule.name), AlertNameLabel: clientmodel.LabelValue(rule.name),
} }
return template.HTML(fmt.Sprintf( return template.HTML(fmt.Sprintf(
`ALERT <a href="%s">%s</a> IF <a href="%s">%s</a> FOR %s WITH %s`, `ALERT <a href="%s">%s</a> IF <a href="%s">%s</a> FOR %s WITH %s`,
@ -214,12 +226,12 @@ func (rule *AlertingRule) ActiveAlerts() []Alert {
} }
// Construct a new AlertingRule. // Construct a new AlertingRule.
func NewAlertingRule(name string, vector ast.VectorNode, holdDuration time.Duration, labels model.LabelSet) *AlertingRule { func NewAlertingRule(name string, vector ast.VectorNode, holdDuration time.Duration, labels clientmodel.LabelSet) *AlertingRule {
return &AlertingRule{ return &AlertingRule{
name: name, name: name,
vector: vector, vector: vector,
holdDuration: holdDuration, holdDuration: holdDuration,
labels: labels, labels: labels,
activeAlerts: map[model.Fingerprint]*Alert{}, activeAlerts: map[clientmodel.Fingerprint]*Alert{},
} }
} }

View file

@ -16,25 +16,29 @@ package ast
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/prometheus/prometheus/model" "hash/fnv"
"github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/metric"
"log" "log"
"math" "math"
"sort" "sort"
"strings"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/metric"
) )
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Raw data value types. // Raw data value types.
type Vector model.Samples type Vector clientmodel.Samples
type Matrix []model.SampleSet
// BUG(julius): Pointerize this.
type Matrix []metric.SampleSet
type groupedAggregation struct { type groupedAggregation struct {
labels model.Metric labels clientmodel.Metric
value model.SampleValue value clientmodel.SampleValue
groupCount int groupCount int
} }
@ -98,7 +102,7 @@ type Node interface {
// interface represents the type returned to the parent node. // interface represents the type returned to the parent node.
type ScalarNode interface { type ScalarNode interface {
Node Node
Eval(timestamp time.Time, view *viewAdapter) model.SampleValue Eval(timestamp time.Time, view *viewAdapter) clientmodel.SampleValue
} }
type VectorNode interface { type VectorNode interface {
@ -123,7 +127,7 @@ type StringNode interface {
type ( type (
// A numeric literal. // A numeric literal.
ScalarLiteral struct { ScalarLiteral struct {
value model.SampleValue value clientmodel.SampleValue
} }
// A function of numeric return type. // A function of numeric return type.
@ -146,9 +150,9 @@ type (
type ( type (
// Vector literal, i.e. metric name plus labelset. // Vector literal, i.e. metric name plus labelset.
VectorLiteral struct { VectorLiteral struct {
labels model.LabelSet labels clientmodel.LabelSet
// Fingerprints are populated from labels at query analysis time. // Fingerprints are populated from labels at query analysis time.
fingerprints model.Fingerprints fingerprints clientmodel.Fingerprints
} }
// A function of vector return type. // A function of vector return type.
@ -160,7 +164,7 @@ type (
// A vector aggregation with vector return type. // A vector aggregation with vector return type.
VectorAggregation struct { VectorAggregation struct {
aggrType AggrType aggrType AggrType
groupBy model.LabelNames groupBy clientmodel.LabelNames
vector VectorNode vector VectorNode
} }
@ -178,9 +182,9 @@ type (
type ( type (
// Matrix literal, i.e. metric name plus labelset and timerange. // Matrix literal, i.e. metric name plus labelset and timerange.
MatrixLiteral struct { MatrixLiteral struct {
labels model.LabelSet labels clientmodel.LabelSet
// Fingerprints are populated from labels at query analysis time. // Fingerprints are populated from labels at query analysis time.
fingerprints model.Fingerprints fingerprints clientmodel.Fingerprints
interval time.Duration interval time.Duration
} }
) )
@ -228,35 +232,48 @@ func (node MatrixLiteral) Children() Nodes { return Nodes{} }
func (node StringLiteral) Children() Nodes { return Nodes{} } func (node StringLiteral) Children() Nodes { return Nodes{} }
func (node StringFunctionCall) Children() Nodes { return node.args } func (node StringFunctionCall) Children() Nodes { return node.args }
func (node *ScalarLiteral) Eval(timestamp time.Time, view *viewAdapter) model.SampleValue { func (node *ScalarLiteral) Eval(timestamp time.Time, view *viewAdapter) clientmodel.SampleValue {
return node.value return node.value
} }
func (node *ScalarArithExpr) Eval(timestamp time.Time, view *viewAdapter) model.SampleValue { func (node *ScalarArithExpr) Eval(timestamp time.Time, view *viewAdapter) clientmodel.SampleValue {
lhs := node.lhs.Eval(timestamp, view) lhs := node.lhs.Eval(timestamp, view)
rhs := node.rhs.Eval(timestamp, view) rhs := node.rhs.Eval(timestamp, view)
return evalScalarBinop(node.opType, lhs, rhs) return evalScalarBinop(node.opType, lhs, rhs)
} }
func (node *ScalarFunctionCall) Eval(timestamp time.Time, view *viewAdapter) model.SampleValue { func (node *ScalarFunctionCall) Eval(timestamp time.Time, view *viewAdapter) clientmodel.SampleValue {
return node.function.callFn(timestamp, view, node.args).(model.SampleValue) return node.function.callFn(timestamp, view, node.args).(clientmodel.SampleValue)
} }
func (node *VectorAggregation) labelsToGroupingKey(labels model.Metric) string { func (node *VectorAggregation) labelsToGroupingKey(labels clientmodel.Metric) uint64 {
keyParts := []string{} summer := fnv.New64a()
for _, keyLabel := range node.groupBy { for _, label := range node.groupBy {
keyParts = append(keyParts, string(labels[keyLabel])) fmt.Fprint(summer, labels[label])
} }
return strings.Join(keyParts, ",") // TODO not safe when label value contains comma.
return summer.Sum64()
} }
func labelsToKey(labels model.Metric) string { func labelsToKey(labels clientmodel.Metric) uint64 {
keyParts := []string{} pairs := metric.LabelPairs{}
for label, value := range labels { for label, value := range labels {
keyParts = append(keyParts, fmt.Sprintf("%v='%v'", label, value)) pairs = append(pairs, &metric.LabelPair{
Name: label,
Value: value,
})
} }
sort.Strings(keyParts)
return strings.Join(keyParts, ",") // TODO not safe when label value contains comma. sort.Sort(pairs)
summer := fnv.New64a()
for _, pair := range pairs {
fmt.Fprint(summer, pair.Name, pair.Value)
}
return summer.Sum64()
} }
func EvalVectorInstant(node VectorNode, timestamp time.Time, storage *metric.TieredStorage, queryStats *stats.TimerGroup) (vector Vector, err error) { func EvalVectorInstant(node VectorNode, timestamp time.Time, storage *metric.TieredStorage, queryStats *stats.TimerGroup) (vector Vector, err error) {
@ -282,19 +299,19 @@ func EvalVectorRange(node VectorNode, start time.Time, end time.Time, interval t
// TODO implement watchdog timer for long-running queries. // TODO implement watchdog timer for long-running queries.
evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start() evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start()
sampleSets := map[string]*model.SampleSet{} sampleSets := map[uint64]*metric.SampleSet{}
for t := start; t.Before(end); t = t.Add(interval) { for t := start; t.Before(end); t = t.Add(interval) {
vector := node.Eval(t, viewAdapter) vector := node.Eval(t, viewAdapter)
for _, sample := range vector { for _, sample := range vector {
samplePair := model.SamplePair{ samplePair := &metric.SamplePair{
Value: sample.Value, Value: sample.Value,
Timestamp: sample.Timestamp, Timestamp: sample.Timestamp,
} }
groupingKey := labelsToKey(sample.Metric) groupingKey := labelsToKey(sample.Metric)
if sampleSets[groupingKey] == nil { if sampleSets[groupingKey] == nil {
sampleSets[groupingKey] = &model.SampleSet{ sampleSets[groupingKey] = &metric.SampleSet{
Metric: sample.Metric, Metric: sample.Metric,
Values: model.Values{samplePair}, Values: metric.Values{samplePair},
} }
} else { } else {
sampleSets[groupingKey].Values = append(sampleSets[groupingKey].Values, samplePair) sampleSets[groupingKey].Values = append(sampleSets[groupingKey].Values, samplePair)
@ -312,8 +329,8 @@ func EvalVectorRange(node VectorNode, start time.Time, end time.Time, interval t
return matrix, nil return matrix, nil
} }
func labelIntersection(metric1, metric2 model.Metric) model.Metric { func labelIntersection(metric1, metric2 clientmodel.Metric) clientmodel.Metric {
intersection := model.Metric{} intersection := clientmodel.Metric{}
for label, value := range metric1 { for label, value := range metric1 {
if metric2[label] == value { if metric2[label] == value {
intersection[label] = value intersection[label] = value
@ -322,18 +339,18 @@ func labelIntersection(metric1, metric2 model.Metric) model.Metric {
return intersection return intersection
} }
func (node *VectorAggregation) groupedAggregationsToVector(aggregations map[string]*groupedAggregation, timestamp time.Time) Vector { func (node *VectorAggregation) groupedAggregationsToVector(aggregations map[uint64]*groupedAggregation, timestamp time.Time) Vector {
vector := Vector{} vector := Vector{}
for _, aggregation := range aggregations { for _, aggregation := range aggregations {
switch node.aggrType { switch node.aggrType {
case AVG: case AVG:
aggregation.value = aggregation.value / model.SampleValue(aggregation.groupCount) aggregation.value = aggregation.value / clientmodel.SampleValue(aggregation.groupCount)
case COUNT: case COUNT:
aggregation.value = model.SampleValue(aggregation.groupCount) aggregation.value = clientmodel.SampleValue(aggregation.groupCount)
default: default:
// For other aggregations, we already have the right value. // For other aggregations, we already have the right value.
} }
sample := model.Sample{ sample := &clientmodel.Sample{
Metric: aggregation.labels, Metric: aggregation.labels,
Value: aggregation.value, Value: aggregation.value,
Timestamp: timestamp, Timestamp: timestamp,
@ -345,7 +362,7 @@ func (node *VectorAggregation) groupedAggregationsToVector(aggregations map[stri
func (node *VectorAggregation) Eval(timestamp time.Time, view *viewAdapter) Vector { func (node *VectorAggregation) Eval(timestamp time.Time, view *viewAdapter) Vector {
vector := node.vector.Eval(timestamp, view) vector := node.vector.Eval(timestamp, view)
result := map[string]*groupedAggregation{} result := map[uint64]*groupedAggregation{}
for _, sample := range vector { for _, sample := range vector {
groupingKey := node.labelsToGroupingKey(sample.Metric) groupingKey := node.labelsToGroupingKey(sample.Metric)
if groupedResult, ok := result[groupingKey]; ok { if groupedResult, ok := result[groupingKey]; ok {
@ -377,6 +394,7 @@ func (node *VectorAggregation) Eval(timestamp time.Time, view *viewAdapter) Vect
} }
} }
} }
return node.groupedAggregationsToVector(result, timestamp) return node.groupedAggregationsToVector(result, timestamp)
} }
@ -394,8 +412,8 @@ func (node *VectorFunctionCall) Eval(timestamp time.Time, view *viewAdapter) Vec
} }
func evalScalarBinop(opType BinOpType, func evalScalarBinop(opType BinOpType,
lhs model.SampleValue, lhs clientmodel.SampleValue,
rhs model.SampleValue) model.SampleValue { rhs clientmodel.SampleValue) clientmodel.SampleValue {
switch opType { switch opType {
case ADD: case ADD:
return lhs + rhs return lhs + rhs
@ -407,13 +425,13 @@ func evalScalarBinop(opType BinOpType,
if rhs != 0 { if rhs != 0 {
return lhs / rhs return lhs / rhs
} else { } else {
return model.SampleValue(math.Inf(int(rhs))) return clientmodel.SampleValue(math.Inf(int(rhs)))
} }
case MOD: case MOD:
if rhs != 0 { if rhs != 0 {
return model.SampleValue(int(lhs) % int(rhs)) return clientmodel.SampleValue(int(lhs) % int(rhs))
} else { } else {
return model.SampleValue(math.Inf(int(rhs))) return clientmodel.SampleValue(math.Inf(int(rhs)))
} }
case EQ: case EQ:
if lhs == rhs { if lhs == rhs {
@ -456,8 +474,8 @@ func evalScalarBinop(opType BinOpType,
} }
func evalVectorBinop(opType BinOpType, func evalVectorBinop(opType BinOpType,
lhs model.SampleValue, lhs clientmodel.SampleValue,
rhs model.SampleValue) (model.SampleValue, bool) { rhs clientmodel.SampleValue) (clientmodel.SampleValue, bool) {
switch opType { switch opType {
case ADD: case ADD:
return lhs + rhs, true return lhs + rhs, true
@ -469,13 +487,13 @@ func evalVectorBinop(opType BinOpType,
if rhs != 0 { if rhs != 0 {
return lhs / rhs, true return lhs / rhs, true
} else { } else {
return model.SampleValue(math.Inf(int(rhs))), true return clientmodel.SampleValue(math.Inf(int(rhs))), true
} }
case MOD: case MOD:
if rhs != 0 { if rhs != 0 {
return model.SampleValue(int(lhs) % int(rhs)), true return clientmodel.SampleValue(int(lhs) % int(rhs)), true
} else { } else {
return model.SampleValue(math.Inf(int(rhs))), true return clientmodel.SampleValue(math.Inf(int(rhs))), true
} }
case EQ: case EQ:
if lhs == rhs { if lhs == rhs {
@ -521,12 +539,12 @@ func evalVectorBinop(opType BinOpType,
panic("Not all enum values enumerated in switch") panic("Not all enum values enumerated in switch")
} }
func labelsEqual(labels1, labels2 model.Metric) bool { func labelsEqual(labels1, labels2 clientmodel.Metric) bool {
if len(labels1) != len(labels2) { if len(labels1) != len(labels2) {
return false return false
} }
for label, value := range labels1 { for label, value := range labels1 {
if labels2[label] != value && label != model.MetricNameLabel { if labels2[label] != value && label != clientmodel.MetricNameLabel {
return false return false
} }
} }
@ -565,7 +583,7 @@ func (node *VectorArithExpr) Eval(timestamp time.Time, view *viewAdapter) Vector
} }
func (node *MatrixLiteral) Eval(timestamp time.Time, view *viewAdapter) Matrix { func (node *MatrixLiteral) Eval(timestamp time.Time, view *viewAdapter) Matrix {
interval := &model.Interval{ interval := &metric.Interval{
OldestInclusive: timestamp.Add(-node.interval), OldestInclusive: timestamp.Add(-node.interval),
NewestInclusive: timestamp, NewestInclusive: timestamp,
} }
@ -578,7 +596,7 @@ func (node *MatrixLiteral) Eval(timestamp time.Time, view *viewAdapter) Matrix {
} }
func (node *MatrixLiteral) EvalBoundaries(timestamp time.Time, view *viewAdapter) Matrix { func (node *MatrixLiteral) EvalBoundaries(timestamp time.Time, view *viewAdapter) Matrix {
interval := &model.Interval{ interval := &metric.Interval{
OldestInclusive: timestamp.Add(-node.interval), OldestInclusive: timestamp.Add(-node.interval),
NewestInclusive: timestamp, NewestInclusive: timestamp,
} }
@ -595,7 +613,7 @@ func (matrix Matrix) Len() int {
} }
func (matrix Matrix) Less(i, j int) bool { func (matrix Matrix) Less(i, j int) bool {
return labelsToKey(matrix[i].Metric) < labelsToKey(matrix[j].Metric) return matrix[i].Metric.String() < matrix[j].Metric.String()
} }
func (matrix Matrix) Swap(i, j int) { func (matrix Matrix) Swap(i, j int) {
@ -613,19 +631,19 @@ func (node *StringFunctionCall) Eval(timestamp time.Time, view *viewAdapter) str
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Constructors. // Constructors.
func NewScalarLiteral(value model.SampleValue) *ScalarLiteral { func NewScalarLiteral(value clientmodel.SampleValue) *ScalarLiteral {
return &ScalarLiteral{ return &ScalarLiteral{
value: value, value: value,
} }
} }
func NewVectorLiteral(labels model.LabelSet) *VectorLiteral { func NewVectorLiteral(labels clientmodel.LabelSet) *VectorLiteral {
return &VectorLiteral{ return &VectorLiteral{
labels: labels, labels: labels,
} }
} }
func NewVectorAggregation(aggrType AggrType, vector VectorNode, groupBy model.LabelNames) *VectorAggregation { func NewVectorAggregation(aggrType AggrType, vector VectorNode, groupBy clientmodel.LabelNames) *VectorAggregation {
return &VectorAggregation{ return &VectorAggregation{
aggrType: aggrType, aggrType: aggrType,
groupBy: groupBy, groupBy: groupBy,

View file

@ -16,10 +16,12 @@ package ast
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/utility"
"sort" "sort"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/utility"
) )
type Function struct { type Function struct {
@ -64,9 +66,9 @@ func (function *Function) CheckArgTypes(args []Node) error {
return nil return nil
} }
// === time() model.SampleValue === // === time() clientmodel.SampleValue ===
func timeImpl(timestamp time.Time, view *viewAdapter, args []Node) interface{} { func timeImpl(timestamp time.Time, view *viewAdapter, args []Node) interface{} {
return model.SampleValue(time.Now().Unix()) return clientmodel.SampleValue(time.Now().Unix())
} }
// === delta(matrix MatrixNode, isCounter ScalarNode) Vector === // === delta(matrix MatrixNode, isCounter ScalarNode) Vector ===
@ -91,8 +93,8 @@ func deltaImpl(timestamp time.Time, view *viewAdapter, args []Node) interface{}
continue continue
} }
counterCorrection := model.SampleValue(0) counterCorrection := clientmodel.SampleValue(0)
lastValue := model.SampleValue(0) lastValue := clientmodel.SampleValue(0)
for _, sample := range samples.Values { for _, sample := range samples.Values {
currentValue := sample.Value currentValue := sample.Value
if isCounter && currentValue < lastValue { if isCounter && currentValue < lastValue {
@ -116,10 +118,10 @@ func deltaImpl(timestamp time.Time, view *viewAdapter, args []Node) interface{}
// them. Depending on how many samples are found under a target interval, // them. Depending on how many samples are found under a target interval,
// the delta results are distorted and temporal aliasing occurs (ugly // the delta results are distorted and temporal aliasing occurs (ugly
// bumps). This effect is corrected for below. // bumps). This effect is corrected for below.
intervalCorrection := model.SampleValue(targetInterval) / model.SampleValue(sampledInterval) intervalCorrection := clientmodel.SampleValue(targetInterval) / clientmodel.SampleValue(sampledInterval)
resultValue *= intervalCorrection resultValue *= intervalCorrection
resultSample := model.Sample{ resultSample := &clientmodel.Sample{
Metric: samples.Metric, Metric: samples.Metric,
Value: resultValue, Value: resultValue,
Timestamp: timestamp, Timestamp: timestamp,
@ -139,7 +141,7 @@ func rateImpl(timestamp time.Time, view *viewAdapter, args []Node) interface{} {
// matrix, such as looking at the samples themselves. // matrix, such as looking at the samples themselves.
interval := args[0].(*MatrixLiteral).interval interval := args[0].(*MatrixLiteral).interval
for i := range vector { for i := range vector {
vector[i].Value /= model.SampleValue(interval / time.Second) vector[i].Value /= clientmodel.SampleValue(interval / time.Second)
} }
return vector return vector
} }
@ -183,69 +185,69 @@ func sortDescImpl(timestamp time.Time, view *viewAdapter, args []Node) interface
// === sampleVectorImpl() Vector === // === sampleVectorImpl() Vector ===
func sampleVectorImpl(timestamp time.Time, view *viewAdapter, args []Node) interface{} { func sampleVectorImpl(timestamp time.Time, view *viewAdapter, args []Node) interface{} {
return Vector{ return Vector{
model.Sample{ &clientmodel.Sample{
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "api-server", clientmodel.JobLabel: "api-server",
"instance": "0", "instance": "0",
}, },
Value: 10, Value: 10,
Timestamp: timestamp, Timestamp: timestamp,
}, },
model.Sample{ &clientmodel.Sample{
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "api-server", clientmodel.JobLabel: "api-server",
"instance": "1", "instance": "1",
}, },
Value: 20, Value: 20,
Timestamp: timestamp, Timestamp: timestamp,
}, },
model.Sample{ &clientmodel.Sample{
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "api-server", clientmodel.JobLabel: "api-server",
"instance": "2", "instance": "2",
}, },
Value: 30, Value: 30,
Timestamp: timestamp, Timestamp: timestamp,
}, },
model.Sample{ &clientmodel.Sample{
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "api-server", clientmodel.JobLabel: "api-server",
"instance": "3", "instance": "3",
"group": "canary", "group": "canary",
}, },
Value: 40, Value: 40,
Timestamp: timestamp, Timestamp: timestamp,
}, },
model.Sample{ &clientmodel.Sample{
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "api-server", clientmodel.JobLabel: "api-server",
"instance": "2", "instance": "2",
"group": "canary", "group": "canary",
}, },
Value: 40, Value: 40,
Timestamp: timestamp, Timestamp: timestamp,
}, },
model.Sample{ &clientmodel.Sample{
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "api-server", clientmodel.JobLabel: "api-server",
"instance": "3", "instance": "3",
"group": "mytest", "group": "mytest",
}, },
Value: 40, Value: 40,
Timestamp: timestamp, Timestamp: timestamp,
}, },
model.Sample{ &clientmodel.Sample{
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "api-server", clientmodel.JobLabel: "api-server",
"instance": "3", "instance": "3",
"group": "mytest", "group": "mytest",
}, },
Value: 40, Value: 40,
Timestamp: timestamp, Timestamp: timestamp,

View file

@ -14,9 +14,12 @@
package ast package ast
import ( import (
"github.com/prometheus/prometheus/model"
"testing" "testing"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/storage/metric"
) )
type emptyRangeNode struct{} type emptyRangeNode struct{}
@ -28,18 +31,18 @@ func (node emptyRangeNode) Children() Nodes { return Nodes{} }
func (node emptyRangeNode) Eval(timestamp time.Time, view *viewAdapter) Matrix { func (node emptyRangeNode) Eval(timestamp time.Time, view *viewAdapter) Matrix {
return Matrix{ return Matrix{
model.SampleSet{ metric.SampleSet{
Metric: model.Metric{model.MetricNameLabel: "empty_metric"}, Metric: clientmodel.Metric{clientmodel.MetricNameLabel: "empty_metric"},
Values: model.Values{}, Values: metric.Values{},
}, },
} }
} }
func (node emptyRangeNode) EvalBoundaries(timestamp time.Time, view *viewAdapter) Matrix { func (node emptyRangeNode) EvalBoundaries(timestamp time.Time, view *viewAdapter) Matrix {
return Matrix{ return Matrix{
model.SampleSet{ metric.SampleSet{
Metric: model.Metric{model.MetricNameLabel: "empty_metric"}, Metric: clientmodel.Metric{clientmodel.MetricNameLabel: "empty_metric"},
Values: model.Values{}, Values: metric.Values{},
}, },
} }
} }

View file

@ -15,10 +15,12 @@ package ast
import ( import (
"flag" "flag"
"github.com/prometheus/prometheus/model" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/stats" "github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/storage/metric"
"time"
) )
var defaultStalenessDelta = flag.Int("defaultStalenessDelta", 300, "Default staleness delta allowance in seconds during expression evaluations.") var defaultStalenessDelta = flag.Int("defaultStalenessDelta", 300, "Default staleness delta allowance in seconds during expression evaluations.")
@ -46,14 +48,14 @@ type viewAdapter struct {
// interpolateSamples interpolates a value at a target time between two // interpolateSamples interpolates a value at a target time between two
// provided sample pairs. // provided sample pairs.
func interpolateSamples(first, second *model.SamplePair, timestamp time.Time) *model.SamplePair { func interpolateSamples(first, second *metric.SamplePair, timestamp time.Time) *metric.SamplePair {
dv := second.Value - first.Value dv := second.Value - first.Value
dt := second.Timestamp.Sub(first.Timestamp) dt := second.Timestamp.Sub(first.Timestamp)
dDt := dv / model.SampleValue(dt) dDt := dv / clientmodel.SampleValue(dt)
offset := model.SampleValue(timestamp.Sub(first.Timestamp)) offset := clientmodel.SampleValue(timestamp.Sub(first.Timestamp))
return &model.SamplePair{ return &metric.SamplePair{
Value: first.Value + (offset * dDt), Value: first.Value + (offset * dDt),
Timestamp: timestamp, Timestamp: timestamp,
} }
@ -63,9 +65,9 @@ func interpolateSamples(first, second *model.SamplePair, timestamp time.Time) *m
// surrounding a given target time. If samples are found both before and after // surrounding a given target time. If samples are found both before and after
// the target time, the sample value is interpolated between these. Otherwise, // the target time, the sample value is interpolated between these. Otherwise,
// the single closest sample is returned verbatim. // the single closest sample is returned verbatim.
func (v *viewAdapter) chooseClosestSample(samples model.Values, timestamp time.Time) *model.SamplePair { func (v *viewAdapter) chooseClosestSample(samples metric.Values, timestamp time.Time) *metric.SamplePair {
var closestBefore *model.SamplePair var closestBefore *metric.SamplePair
var closestAfter *model.SamplePair var closestAfter *metric.SamplePair
for _, candidate := range samples { for _, candidate := range samples {
delta := candidate.Timestamp.Sub(timestamp) delta := candidate.Timestamp.Sub(timestamp)
// Samples before target time. // Samples before target time.
@ -79,7 +81,7 @@ func (v *viewAdapter) chooseClosestSample(samples model.Values, timestamp time.T
continue continue
} }
sample := candidate sample := candidate
closestBefore = &sample closestBefore = sample
} }
// Samples after target time. // Samples after target time.
@ -93,7 +95,7 @@ func (v *viewAdapter) chooseClosestSample(samples model.Values, timestamp time.T
continue continue
} }
sample := candidate sample := candidate
closestAfter = &sample closestAfter = sample
} }
} }
@ -107,7 +109,7 @@ func (v *viewAdapter) chooseClosestSample(samples model.Values, timestamp time.T
} }
} }
func (v *viewAdapter) GetValueAtTime(fingerprints model.Fingerprints, timestamp time.Time) (samples Vector, err error) { func (v *viewAdapter) GetValueAtTime(fingerprints clientmodel.Fingerprints, timestamp time.Time) (samples Vector, err error) {
timer := v.stats.GetTimer(stats.GetValueAtTimeTime).Start() timer := v.stats.GetTimer(stats.GetValueAtTimeTime).Start()
for _, fingerprint := range fingerprints { for _, fingerprint := range fingerprints {
sampleCandidates := v.view.GetValueAtTime(fingerprint, timestamp) sampleCandidates := v.view.GetValueAtTime(fingerprint, timestamp)
@ -117,7 +119,7 @@ func (v *viewAdapter) GetValueAtTime(fingerprints model.Fingerprints, timestamp
continue continue
} }
if samplePair != nil { if samplePair != nil {
samples = append(samples, model.Sample{ samples = append(samples, &clientmodel.Sample{
Metric: m, Metric: m,
Value: samplePair.Value, Value: samplePair.Value,
Timestamp: timestamp, Timestamp: timestamp,
@ -128,7 +130,7 @@ func (v *viewAdapter) GetValueAtTime(fingerprints model.Fingerprints, timestamp
return samples, err return samples, err
} }
func (v *viewAdapter) GetBoundaryValues(fingerprints model.Fingerprints, interval *model.Interval) (sampleSets []model.SampleSet, err error) { func (v *viewAdapter) GetBoundaryValues(fingerprints clientmodel.Fingerprints, interval *metric.Interval) (sampleSets []metric.SampleSet, err error) {
timer := v.stats.GetTimer(stats.GetBoundaryValuesTime).Start() timer := v.stats.GetTimer(stats.GetBoundaryValuesTime).Start()
for _, fingerprint := range fingerprints { for _, fingerprint := range fingerprints {
samplePairs := v.view.GetBoundaryValues(fingerprint, *interval) samplePairs := v.view.GetBoundaryValues(fingerprint, *interval)
@ -142,7 +144,7 @@ func (v *viewAdapter) GetBoundaryValues(fingerprints model.Fingerprints, interva
continue continue
} }
sampleSet := model.SampleSet{ sampleSet := metric.SampleSet{
Metric: m, Metric: m,
Values: samplePairs, Values: samplePairs,
} }
@ -152,7 +154,7 @@ func (v *viewAdapter) GetBoundaryValues(fingerprints model.Fingerprints, interva
return sampleSets, nil return sampleSets, nil
} }
func (v *viewAdapter) GetRangeValues(fingerprints model.Fingerprints, interval *model.Interval) (sampleSets []model.SampleSet, err error) { func (v *viewAdapter) GetRangeValues(fingerprints clientmodel.Fingerprints, interval *metric.Interval) (sampleSets []metric.SampleSet, err error) {
timer := v.stats.GetTimer(stats.GetRangeValuesTime).Start() timer := v.stats.GetTimer(stats.GetRangeValuesTime).Start()
for _, fingerprint := range fingerprints { for _, fingerprint := range fingerprints {
samplePairs := v.view.GetRangeValues(fingerprint, *interval) samplePairs := v.view.GetRangeValues(fingerprint, *interval)
@ -166,7 +168,7 @@ func (v *viewAdapter) GetRangeValues(fingerprints model.Fingerprints, interval *
continue continue
} }
sampleSet := model.SampleSet{ sampleSet := metric.SampleSet{
Metric: m, Metric: m,
Values: samplePairs, Values: samplePairs,
} }

View file

@ -16,13 +16,15 @@ package ast
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/metric"
"github.com/prometheus/prometheus/utility"
"sort" "sort"
"strings" "strings"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/metric"
"github.com/prometheus/prometheus/utility"
) )
type OutputFormat int type OutputFormat int
@ -86,13 +88,13 @@ func (vector Vector) String() string {
func (matrix Matrix) String() string { func (matrix Matrix) String() string {
metricStrings := make([]string, 0, len(matrix)) metricStrings := make([]string, 0, len(matrix))
for _, sampleSet := range matrix { for _, sampleSet := range matrix {
metricName, ok := sampleSet.Metric[model.MetricNameLabel] metricName, ok := sampleSet.Metric[clientmodel.MetricNameLabel]
if !ok { if !ok {
panic("Tried to print matrix without metric name") panic("Tried to print matrix without metric name")
} }
labelStrings := make([]string, 0, len(sampleSet.Metric)-1) labelStrings := make([]string, 0, len(sampleSet.Metric)-1)
for label, value := range sampleSet.Metric { for label, value := range sampleSet.Metric {
if label != model.MetricNameLabel { if label != clientmodel.MetricNameLabel {
labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value)) labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
} }
} }
@ -296,13 +298,13 @@ func (node *ScalarArithExpr) String() string {
} }
func (node *VectorLiteral) String() string { func (node *VectorLiteral) String() string {
metricName, ok := node.labels[model.MetricNameLabel] metricName, ok := node.labels[clientmodel.MetricNameLabel]
if !ok { if !ok {
panic("Tried to print vector without metric name") panic("Tried to print vector without metric name")
} }
labelStrings := make([]string, 0, len(node.labels)-1) labelStrings := make([]string, 0, len(node.labels)-1)
for label, value := range node.labels { for label, value := range node.labels {
if label != model.MetricNameLabel { if label != clientmodel.MetricNameLabel {
labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value)) labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
} }
} }

View file

@ -14,15 +14,17 @@
package ast package ast
import ( import (
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/metric"
"log" "log"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/metric"
) )
type FullRangeMap map[model.Fingerprint]time.Duration type FullRangeMap map[clientmodel.Fingerprint]time.Duration
type IntervalRangeMap map[model.Fingerprint]bool type IntervalRangeMap map[clientmodel.Fingerprint]bool
type QueryAnalyzer struct { type QueryAnalyzer struct {
// Values collected by query analysis. // Values collected by query analysis.
@ -105,10 +107,10 @@ func viewAdapterForInstantQuery(node Node, timestamp time.Time, storage *metric.
requestBuildTimer := queryStats.GetTimer(stats.ViewRequestBuildTime).Start() requestBuildTimer := queryStats.GetTimer(stats.ViewRequestBuildTime).Start()
viewBuilder := metric.NewViewRequestBuilder() viewBuilder := metric.NewViewRequestBuilder()
for fingerprint, rangeDuration := range analyzer.FullRanges { for fingerprint, rangeDuration := range analyzer.FullRanges {
viewBuilder.GetMetricRange(fingerprint, timestamp.Add(-rangeDuration), timestamp) viewBuilder.GetMetricRange(&fingerprint, timestamp.Add(-rangeDuration), timestamp)
} }
for fingerprint := range analyzer.IntervalRanges { for fingerprint := range analyzer.IntervalRanges {
viewBuilder.GetMetricAtTime(fingerprint, timestamp) viewBuilder.GetMetricAtTime(&fingerprint, timestamp)
} }
requestBuildTimer.Stop() requestBuildTimer.Stop()
@ -132,11 +134,11 @@ func viewAdapterForRangeQuery(node Node, start time.Time, end time.Time, interva
for fingerprint, rangeDuration := range analyzer.FullRanges { for fingerprint, rangeDuration := range analyzer.FullRanges {
// TODO: we should support GetMetricRangeAtInterval() or similar ops in the view builder. // TODO: we should support GetMetricRangeAtInterval() or similar ops in the view builder.
for t := start; t.Before(end); t = t.Add(interval) { for t := start; t.Before(end); t = t.Add(interval) {
viewBuilder.GetMetricRange(fingerprint, t.Add(-rangeDuration), t) viewBuilder.GetMetricRange(&fingerprint, t.Add(-rangeDuration), t)
} }
} }
for fingerprint := range analyzer.IntervalRanges { for fingerprint := range analyzer.IntervalRanges {
viewBuilder.GetMetricAtInterval(fingerprint, start, end, interval) viewBuilder.GetMetricAtInterval(&fingerprint, start, end, interval)
} }
requestBuildTimer.Stop() requestBuildTimer.Stop()

View file

@ -17,19 +17,20 @@ import (
"fmt" "fmt"
"html" "html"
"github.com/prometheus/prometheus/model" clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/rules/ast" "github.com/prometheus/prometheus/rules/ast"
"github.com/prometheus/prometheus/utility" "github.com/prometheus/prometheus/utility"
) )
func CreateRecordingRule(name string, labels model.LabelSet, expr ast.Node, permanent bool) (*RecordingRule, error) { func CreateRecordingRule(name string, labels clientmodel.LabelSet, expr ast.Node, permanent bool) (*RecordingRule, error) {
if _, ok := expr.(ast.VectorNode); !ok { if _, ok := expr.(ast.VectorNode); !ok {
return nil, fmt.Errorf("Recording rule expression %v does not evaluate to vector type", expr) return nil, fmt.Errorf("Recording rule expression %v does not evaluate to vector type", expr)
} }
return NewRecordingRule(name, labels, expr.(ast.VectorNode), permanent), nil return NewRecordingRule(name, labels, expr.(ast.VectorNode), permanent), nil
} }
func CreateAlertingRule(name string, expr ast.Node, holdDurationStr string, labels model.LabelSet) (*AlertingRule, error) { func CreateAlertingRule(name string, expr ast.Node, holdDurationStr string, labels clientmodel.LabelSet) (*AlertingRule, error) {
if _, ok := expr.(ast.VectorNode); !ok { if _, ok := expr.(ast.VectorNode); !ok {
return nil, fmt.Errorf("Alert rule expression %v does not evaluate to vector type", expr) return nil, fmt.Errorf("Alert rule expression %v does not evaluate to vector type", expr)
} }
@ -52,7 +53,7 @@ func NewFunctionCall(name string, args []ast.Node) (ast.Node, error) {
return functionCall, nil return functionCall, nil
} }
func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy model.LabelNames) (*ast.VectorAggregation, error) { func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy clientmodel.LabelNames) (*ast.VectorAggregation, error) {
if _, ok := vector.(ast.VectorNode); !ok { if _, ok := vector.(ast.VectorNode); !ok {
return nil, fmt.Errorf("Operand of %v aggregation must be of vector type", aggrTypeStr) return nil, fmt.Errorf("Operand of %v aggregation must be of vector type", aggrTypeStr)
} }

View file

@ -14,19 +14,21 @@
package rules package rules
import ( import (
"github.com/prometheus/prometheus/model" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/rules/ast" "github.com/prometheus/prometheus/rules/ast"
"github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/storage/metric"
"time"
) )
var testSampleInterval = time.Duration(5) * time.Minute var testSampleInterval = time.Duration(5) * time.Minute
var testStartTime = time.Time{} var testStartTime = time.Time{}
func getTestValueStream(startVal model.SampleValue, endVal model.SampleValue, stepVal model.SampleValue, startTime time.Time) (resultValues model.Values) { func getTestValueStream(startVal clientmodel.SampleValue, endVal clientmodel.SampleValue, stepVal clientmodel.SampleValue, startTime time.Time) (resultValues metric.Values) {
currentTime := startTime currentTime := startTime
for currentVal := startVal; currentVal <= endVal; currentVal += stepVal { for currentVal := startVal; currentVal <= endVal; currentVal += stepVal {
sample := model.SamplePair{ sample := &metric.SamplePair{
Value: currentVal, Value: currentVal,
Timestamp: currentTime, Timestamp: currentTime,
} }
@ -40,7 +42,7 @@ func getTestVectorFromTestMatrix(matrix ast.Matrix) ast.Vector {
vector := ast.Vector{} vector := ast.Vector{}
for _, sampleSet := range matrix { for _, sampleSet := range matrix {
lastSample := sampleSet.Values[len(sampleSet.Values)-1] lastSample := sampleSet.Values[len(sampleSet.Values)-1]
vector = append(vector, model.Sample{ vector = append(vector, &clientmodel.Sample{
Metric: sampleSet.Metric, Metric: sampleSet.Metric,
Value: lastSample.Value, Value: lastSample.Value,
Timestamp: lastSample.Timestamp, Timestamp: lastSample.Timestamp,
@ -50,10 +52,10 @@ func getTestVectorFromTestMatrix(matrix ast.Matrix) ast.Vector {
} }
func storeMatrix(storage metric.TieredStorage, matrix ast.Matrix) (err error) { func storeMatrix(storage metric.TieredStorage, matrix ast.Matrix) (err error) {
pendingSamples := model.Samples{} pendingSamples := clientmodel.Samples{}
for _, sampleSet := range matrix { for _, sampleSet := range matrix {
for _, sample := range sampleSet.Values { for _, sample := range sampleSet.Values {
pendingSamples = append(pendingSamples, model.Sample{ pendingSamples = append(pendingSamples, &clientmodel.Sample{
Metric: sampleSet.Metric, Metric: sampleSet.Metric,
Value: sample.Value, Value: sample.Value,
Timestamp: sample.Timestamp, Timestamp: sample.Timestamp,
@ -66,96 +68,96 @@ func storeMatrix(storage metric.TieredStorage, matrix ast.Matrix) (err error) {
var testMatrix = ast.Matrix{ var testMatrix = ast.Matrix{
{ {
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "api-server", clientmodel.JobLabel: "api-server",
"instance": "0", "instance": "0",
"group": "production", "group": "production",
}, },
Values: getTestValueStream(0, 100, 10, testStartTime), Values: getTestValueStream(0, 100, 10, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "api-server", clientmodel.JobLabel: "api-server",
"instance": "1", "instance": "1",
"group": "production", "group": "production",
}, },
Values: getTestValueStream(0, 200, 20, testStartTime), Values: getTestValueStream(0, 200, 20, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "api-server", clientmodel.JobLabel: "api-server",
"instance": "0", "instance": "0",
"group": "canary", "group": "canary",
}, },
Values: getTestValueStream(0, 300, 30, testStartTime), Values: getTestValueStream(0, 300, 30, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "api-server", clientmodel.JobLabel: "api-server",
"instance": "1", "instance": "1",
"group": "canary", "group": "canary",
}, },
Values: getTestValueStream(0, 400, 40, testStartTime), Values: getTestValueStream(0, 400, 40, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "app-server", clientmodel.JobLabel: "app-server",
"instance": "0", "instance": "0",
"group": "production", "group": "production",
}, },
Values: getTestValueStream(0, 500, 50, testStartTime), Values: getTestValueStream(0, 500, 50, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "app-server", clientmodel.JobLabel: "app-server",
"instance": "1", "instance": "1",
"group": "production", "group": "production",
}, },
Values: getTestValueStream(0, 600, 60, testStartTime), Values: getTestValueStream(0, 600, 60, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "app-server", clientmodel.JobLabel: "app-server",
"instance": "0", "instance": "0",
"group": "canary", "group": "canary",
}, },
Values: getTestValueStream(0, 700, 70, testStartTime), Values: getTestValueStream(0, 700, 70, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
model.JobLabel: "app-server", clientmodel.JobLabel: "app-server",
"instance": "1", "instance": "1",
"group": "canary", "group": "canary",
}, },
Values: getTestValueStream(0, 800, 80, testStartTime), Values: getTestValueStream(0, 800, 80, testStartTime),
}, },
// Single-letter metric and label names. // Single-letter metric and label names.
{ {
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "x", clientmodel.MetricNameLabel: "x",
"y": "testvalue", "y": "testvalue",
}, },
Values: getTestValueStream(0, 100, 10, testStartTime), Values: getTestValueStream(0, 100, 10, testStartTime),
}, },
// Counter reset in the middle of range. // Counter reset in the middle of range.
{ {
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "testcounter_reset_middle", clientmodel.MetricNameLabel: "testcounter_reset_middle",
}, },
Values: append(getTestValueStream(0, 40, 10, testStartTime), getTestValueStream(0, 50, 10, testStartTime.Add(testSampleInterval*5))...), Values: append(getTestValueStream(0, 40, 10, testStartTime), getTestValueStream(0, 50, 10, testStartTime.Add(testSampleInterval*5))...),
}, },
// Counter reset at the end of range. // Counter reset at the end of range.
{ {
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "testcounter_reset_end", clientmodel.MetricNameLabel: "testcounter_reset_end",
}, },
Values: append(getTestValueStream(0, 90, 10, testStartTime), getTestValueStream(0, 0, 10, testStartTime.Add(testSampleInterval*10))...), Values: append(getTestValueStream(0, 90, 10, testStartTime), getTestValueStream(0, 0, 10, testStartTime.Add(testSampleInterval*10))...),
}, },

View file

@ -15,9 +15,10 @@
package rules package rules
import ( import (
"github.com/prometheus/prometheus/model"
"strconv" "strconv"
"strings" "strings"
clientmodel "github.com/prometheus/client_golang/model"
) )
%} %}
@ -58,7 +59,7 @@ avg|sum|max|min|count { yylval.str = strings.ToUpper(yytext); return AGGR_OP
if (err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax) { if (err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax) {
panic("Invalid float") panic("Invalid float")
} }
yylval.num = model.SampleValue(num) yylval.num = clientmodel.SampleValue(num)
return NUMBER } return NUMBER }
\"(\\.|[^\\"])*\" { yylval.str = yytext[1:len(yytext) - 1]; return STRING } \"(\\.|[^\\"])*\" { yylval.str = yytext[1:len(yytext) - 1]; return STRING }

View file

@ -1,7 +1,6 @@
// Generated by golex // Generated by golex
package rules package rules
import ( import (
"bufio" "bufio"
"io" "io"
@ -10,9 +9,10 @@ import (
"sort" "sort"
) )
import ( import (
"github.com/prometheus/prometheus/model" "strconv"
"strconv" "strings"
"strings"
clientmodel "github.com/prometheus/client_golang/model"
) )
var yyin io.Reader = os.Stdin var yyin io.Reader = os.Stdin
@ -32,6 +32,7 @@ type yyactionreturn struct {
} }
type yyactionreturntype int type yyactionreturntype int
const ( const (
yyRT_FALLTHROUGH yyactionreturntype = iota yyRT_FALLTHROUGH yyactionreturntype = iota
yyRT_USER_RETURN yyRT_USER_RETURN
@ -44,6 +45,7 @@ var yyorigidx int
var yytext string = "" var yytext string = ""
var yytextrepl bool = true var yytextrepl bool = true
func yymore() { func yymore() {
yytextrepl = false yytextrepl = false
} }
@ -61,6 +63,7 @@ func yyREJECT() {
} }
var yylessed int var yylessed int
func yyless(n int) { func yyless(n int) {
yylessed = len(yytext) - n yylessed = len(yytext) - n
} }
@ -81,13 +84,14 @@ func input() int {
} }
var EOF int = -1 var EOF int = -1
type yystartcondition int type yystartcondition int
var INITIAL yystartcondition = 0 var INITIAL yystartcondition = 0
var YY_START yystartcondition = INITIAL var YY_START yystartcondition = INITIAL
type yylexMatch struct { type yylexMatch struct {
index int index int
matchFunc func() yyactionreturn matchFunc func() yyactionreturn
sortLen int sortLen int
advLen int advLen int
@ -221,8 +225,9 @@ func yylex() int {
return 0 return 0
} }
var S_COMMENTS yystartcondition = 1024 var S_COMMENTS yystartcondition = 1024
var yystartconditionexclmap = map[yystartcondition]bool{S_COMMENTS: true, } var yystartconditionexclmap = map[yystartcondition]bool{S_COMMENTS: true}
var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) { var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -265,7 +270,7 @@ var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcon
yyBEGIN(S_COMMENTS) yyBEGIN(S_COMMENTS)
} }
return yyactionreturn{0, yyRT_FALLTHROUGH} return yyactionreturn{0, yyRT_FALLTHROUGH}
}}, {regexp.MustCompile("\\*/"), nil, []yystartcondition{S_COMMENTS, }, false, func() (yyar yyactionreturn) { }}, {regexp.MustCompile("\\*/"), nil, []yystartcondition{S_COMMENTS}, false, func() (yyar yyactionreturn) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
if r != "yyREJECT" { if r != "yyREJECT" {
@ -278,7 +283,7 @@ var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcon
yyBEGIN(0) yyBEGIN(0)
} }
return yyactionreturn{0, yyRT_FALLTHROUGH} return yyactionreturn{0, yyRT_FALLTHROUGH}
}}, {regexp.MustCompile("[^\\n]"), nil, []yystartcondition{S_COMMENTS, }, false, func() (yyar yyactionreturn) { }}, {regexp.MustCompile("[^\\n]"), nil, []yystartcondition{S_COMMENTS}, false, func() (yyar yyactionreturn) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
if r != "yyREJECT" { if r != "yyREJECT" {
@ -506,7 +511,7 @@ var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcon
if err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax { if err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax {
panic("Invalid float") panic("Invalid float")
} }
yylval.num = model.SampleValue(num) yylval.num = clientmodel.SampleValue(num)
return yyactionreturn{NUMBER, yyRT_USER_RETURN} return yyactionreturn{NUMBER, yyRT_USER_RETURN}
} }
return yyactionreturn{0, yyRT_FALLTHROUGH} return yyactionreturn{0, yyRT_FALLTHROUGH}
@ -575,5 +580,6 @@ var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcon
{ {
} }
return yyactionreturn{0, yyRT_FALLTHROUGH} return yyactionreturn{0, yyRT_FALLTHROUGH}
}}, } }}}
func yyactioninline(BEGIN func(yystartcondition)) {} func yyactioninline(BEGIN func(yystartcondition)) {}

View file

@ -14,18 +14,16 @@
package rules package rules
import ( import (
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/storage/metric"
"log" "log"
"sync" "sync"
"time" "time"
)
type Result struct { "github.com/prometheus/client_golang/extraction"
Err error // TODO propagate errors from rule evaluation. clientmodel "github.com/prometheus/client_golang/model"
Samples model.Samples
} "github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/storage/metric"
)
type RuleManager interface { type RuleManager interface {
// Load and add rules from rule files specified in the configuration. // Load and add rules from rule files specified in the configuration.
@ -45,13 +43,13 @@ type ruleManager struct {
sync.Mutex sync.Mutex
rules []Rule rules []Rule
results chan *Result results chan<- *extraction.Result
done chan bool done chan bool
interval time.Duration interval time.Duration
storage *metric.TieredStorage storage *metric.TieredStorage
} }
func NewRuleManager(results chan *Result, interval time.Duration, storage *metric.TieredStorage) RuleManager { func NewRuleManager(results chan<- *extraction.Result, interval time.Duration, storage *metric.TieredStorage) RuleManager {
manager := &ruleManager{ manager := &ruleManager{
results: results, results: results,
rules: []Rule{}, rules: []Rule{},
@ -86,7 +84,7 @@ func (m *ruleManager) Stop() {
} }
} }
func (m *ruleManager) runIteration(results chan *Result) { func (m *ruleManager) runIteration(results chan<- *extraction.Result) {
now := time.Now() now := time.Now()
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
@ -101,9 +99,9 @@ func (m *ruleManager) runIteration(results chan *Result) {
go func(rule Rule) { go func(rule Rule) {
defer wg.Done() defer wg.Done()
vector, err := rule.Eval(now, m.storage) vector, err := rule.Eval(now, m.storage)
samples := make(model.Samples, len(vector)) samples := make(clientmodel.Samples, len(vector))
copy(samples, vector) copy(samples, vector)
m.results <- &Result{ m.results <- &extraction.Result{
Samples: samples, Samples: samples,
Err: err, Err: err,
} }

View file

@ -14,18 +14,21 @@
%{ %{
package rules package rules
import "github.com/prometheus/prometheus/model" import (
clientmodel "github.com/prometheus/client_golang/model"
import "github.com/prometheus/prometheus/rules/ast" import "github.com/prometheus/prometheus/rules/ast"
)
%} %}
%union { %union {
num model.SampleValue num clientmodel.SampleValue
str string str string
ruleNode ast.Node ruleNode ast.Node
ruleNodeSlice []ast.Node ruleNodeSlice []ast.Node
boolean bool boolean bool
labelNameSlice model.LabelNames labelNameSlice clientmodel.LabelNames
labelSet model.LabelSet labelSet clientmodel.LabelSet
} }
/* We simulate multiple start symbols for closely-related grammars via dummy tokens. See /* We simulate multiple start symbols for closely-related grammars via dummy tokens. See
@ -93,11 +96,11 @@ qualifier : /* empty */
; ;
rule_labels : /* empty */ rule_labels : /* empty */
{ $$ = model.LabelSet{} } { $$ = clientmodel.LabelSet{} }
| '{' label_assign_list '}' | '{' label_assign_list '}'
{ $$ = $2 } { $$ = $2 }
| '{' '}' | '{' '}'
{ $$ = model.LabelSet{} } { $$ = clientmodel.LabelSet{} }
label_assign_list : label_assign label_assign_list : label_assign
{ $$ = $1 } { $$ = $1 }
@ -106,14 +109,14 @@ label_assign_list : label_assign
; ;
label_assign : IDENTIFIER '=' STRING label_assign : IDENTIFIER '=' STRING
{ $$ = model.LabelSet{ model.LabelName($1): model.LabelValue($3) } } { $$ = clientmodel.LabelSet{ clientmodel.LabelName($1): clientmodel.LabelValue($3) } }
; ;
rule_expr : '(' rule_expr ')' rule_expr : '(' rule_expr ')'
{ $$ = $2 } { $$ = $2 }
| IDENTIFIER rule_labels | IDENTIFIER rule_labels
{ $2[model.MetricNameLabel] = model.LabelValue($1); $$ = ast.NewVectorLiteral($2) } { $2[clientmodel.MetricNameLabel] = clientmodel.LabelValue($1); $$ = ast.NewVectorLiteral($2) }
| IDENTIFIER '(' func_arg_list ')' | IDENTIFIER '(' func_arg_list ')'
{ {
var err error var err error
@ -163,15 +166,15 @@ rule_expr : '(' rule_expr ')'
; ;
grouping_opts : grouping_opts :
{ $$ = model.LabelNames{} } { $$ = clientmodel.LabelNames{} }
| GROUP_OP '(' label_list ')' | GROUP_OP '(' label_list ')'
{ $$ = $3 } { $$ = $3 }
; ;
label_list : IDENTIFIER label_list : IDENTIFIER
{ $$ = model.LabelNames{model.LabelName($1)} } { $$ = clientmodel.LabelNames{clientmodel.LabelName($1)} }
| label_list ',' IDENTIFIER | label_list ',' IDENTIFIER
{ $$ = append($$, model.LabelName($3)) } { $$ = append($$, clientmodel.LabelName($3)) }
; ;
func_arg_list : func_arg func_arg_list : func_arg

View file

@ -1,22 +1,22 @@
//line parser.y:15
package rules
import __yyfmt__ "fmt"
//line parser.y:15 //line parser.y:15
package rules import clientmodel "github.com/prometheus/client_golang/model"
import __yyfmt__ "fmt" import "github.com/prometheus/prometheus/rules/ast"
//line parser.y:15
import "github.com/prometheus/prometheus/model"
import "github.com/prometheus/prometheus/rules/ast"
//line parser.y:21 //line parser.y:21
type yySymType struct { type yySymType struct {
yys int yys int
num model.SampleValue num clientmodel.SampleValue
str string str string
ruleNode ast.Node ruleNode ast.Node
ruleNodeSlice []ast.Node ruleNodeSlice []ast.Node
boolean bool boolean bool
labelNameSlice model.LabelNames labelNameSlice clientmodel.LabelNames
labelSet model.LabelSet labelSet clientmodel.LabelSet
} }
const START_RULES = 57346 const START_RULES = 57346
@ -63,7 +63,6 @@ const yyMaxDepth = 200
//line parser.y:188 //line parser.y:188
//line yacctab:1 //line yacctab:1
var yyExca = []int{ var yyExca = []int{
-1, 1, -1, 1,
@ -396,133 +395,207 @@ yydefault:
case 5: case 5:
//line parser.y:66 //line parser.y:66
{ yylex.(*RulesLexer).parsedExpr = yyS[yypt-0].ruleNode } {
yylex.(*RulesLexer).parsedExpr = yyS[yypt-0].ruleNode
}
case 6: case 6:
//line parser.y:70 //line parser.y:70
{ {
rule, err := CreateRecordingRule(yyS[yypt-3].str, yyS[yypt-2].labelSet, yyS[yypt-0].ruleNode, yyS[yypt-4].boolean) rule, err := CreateRecordingRule(yyS[yypt-3].str, yyS[yypt-2].labelSet, yyS[yypt-0].ruleNode, yyS[yypt-4].boolean)
if err != nil { yylex.Error(err.Error()); return 1 } if err != nil {
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule) yylex.Error(err.Error())
} return 1
}
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
}
case 7: case 7:
//line parser.y:76 //line parser.y:76
{ {
rule, err := CreateAlertingRule(yyS[yypt-5].str, yyS[yypt-3].ruleNode, yyS[yypt-2].str, yyS[yypt-0].labelSet) rule, err := CreateAlertingRule(yyS[yypt-5].str, yyS[yypt-3].ruleNode, yyS[yypt-2].str, yyS[yypt-0].labelSet)
if err != nil { yylex.Error(err.Error()); return 1 } if err != nil {
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule) yylex.Error(err.Error())
} return 1
}
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
}
case 8: case 8:
//line parser.y:84 //line parser.y:84
{ yyVAL.str = "0s" } {
yyVAL.str = "0s"
}
case 9: case 9:
//line parser.y:86 //line parser.y:86
{ yyVAL.str = yyS[yypt-0].str } {
yyVAL.str = yyS[yypt-0].str
}
case 10: case 10:
//line parser.y:90 //line parser.y:90
{ yyVAL.boolean = false } {
yyVAL.boolean = false
}
case 11: case 11:
//line parser.y:92 //line parser.y:92
{ yyVAL.boolean = true } {
yyVAL.boolean = true
}
case 12: case 12:
//line parser.y:96 //line parser.y:96
{ yyVAL.labelSet = model.LabelSet{} } {
yyVAL.labelSet = clientmodel.LabelSet{}
}
case 13: case 13:
//line parser.y:98 //line parser.y:98
{ yyVAL.labelSet = yyS[yypt-1].labelSet } {
yyVAL.labelSet = yyS[yypt-1].labelSet
}
case 14: case 14:
//line parser.y:100 //line parser.y:100
{ yyVAL.labelSet = model.LabelSet{} } {
yyVAL.labelSet = clientmodel.LabelSet{}
}
case 15: case 15:
//line parser.y:103 //line parser.y:103
{ yyVAL.labelSet = yyS[yypt-0].labelSet } {
yyVAL.labelSet = yyS[yypt-0].labelSet
}
case 16: case 16:
//line parser.y:105 //line parser.y:105
{ for k, v := range yyS[yypt-0].labelSet { yyVAL.labelSet[k] = v } } {
for k, v := range yyS[yypt-0].labelSet {
yyVAL.labelSet[k] = v
}
}
case 17: case 17:
//line parser.y:109 //line parser.y:109
{ yyVAL.labelSet = model.LabelSet{ model.LabelName(yyS[yypt-2].str): model.LabelValue(yyS[yypt-0].str) } } {
yyVAL.labelSet = clientmodel.LabelSet{clientmodel.LabelName(yyS[yypt-2].str): clientmodel.LabelValue(yyS[yypt-0].str)}
}
case 18: case 18:
//line parser.y:114 //line parser.y:114
{ yyVAL.ruleNode = yyS[yypt-1].ruleNode } {
yyVAL.ruleNode = yyS[yypt-1].ruleNode
}
case 19: case 19:
//line parser.y:116 //line parser.y:116
{ yyS[yypt-0].labelSet[model.MetricNameLabel] = model.LabelValue(yyS[yypt-1].str); yyVAL.ruleNode = ast.NewVectorLiteral(yyS[yypt-0].labelSet) } {
yyS[yypt-0].labelSet[clientmodel.MetricNameLabel] = clientmodel.LabelValue(yyS[yypt-1].str)
yyVAL.ruleNode = ast.NewVectorLiteral(yyS[yypt-0].labelSet)
}
case 20: case 20:
//line parser.y:118 //line parser.y:118
{ {
var err error var err error
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice) yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice)
if err != nil { yylex.Error(err.Error()); return 1 } if err != nil {
} yylex.Error(err.Error())
return 1
}
}
case 21: case 21:
//line parser.y:124 //line parser.y:124
{ {
var err error var err error
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{}) yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{})
if err != nil { yylex.Error(err.Error()); return 1 } if err != nil {
} yylex.Error(err.Error())
return 1
}
}
case 22: case 22:
//line parser.y:130 //line parser.y:130
{ {
var err error var err error
yyVAL.ruleNode, err = NewMatrix(yyS[yypt-3].ruleNode, yyS[yypt-1].str) yyVAL.ruleNode, err = NewMatrix(yyS[yypt-3].ruleNode, yyS[yypt-1].str)
if err != nil { yylex.Error(err.Error()); return 1 } if err != nil {
} yylex.Error(err.Error())
return 1
}
}
case 23: case 23:
//line parser.y:136 //line parser.y:136
{ {
var err error var err error
yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-4].str, yyS[yypt-2].ruleNode, yyS[yypt-0].labelNameSlice) yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-4].str, yyS[yypt-2].ruleNode, yyS[yypt-0].labelNameSlice)
if err != nil { yylex.Error(err.Error()); return 1 } if err != nil {
} yylex.Error(err.Error())
return 1
}
}
case 24: case 24:
//line parser.y:144 //line parser.y:144
{ {
var err error var err error
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode) yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
if err != nil { yylex.Error(err.Error()); return 1 } if err != nil {
} yylex.Error(err.Error())
return 1
}
}
case 25: case 25:
//line parser.y:150 //line parser.y:150
{ {
var err error var err error
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode) yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
if err != nil { yylex.Error(err.Error()); return 1 } if err != nil {
} yylex.Error(err.Error())
return 1
}
}
case 26: case 26:
//line parser.y:156 //line parser.y:156
{ {
var err error var err error
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode) yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
if err != nil { yylex.Error(err.Error()); return 1 } if err != nil {
} yylex.Error(err.Error())
return 1
}
}
case 27: case 27:
//line parser.y:162 //line parser.y:162
{ yyVAL.ruleNode = ast.NewScalarLiteral(yyS[yypt-0].num)} {
yyVAL.ruleNode = ast.NewScalarLiteral(yyS[yypt-0].num)
}
case 28: case 28:
//line parser.y:166 //line parser.y:166
{ yyVAL.labelNameSlice = model.LabelNames{} } {
yyVAL.labelNameSlice = clientmodel.LabelNames{}
}
case 29: case 29:
//line parser.y:168 //line parser.y:168
{ yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice } {
yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice
}
case 30: case 30:
//line parser.y:172 //line parser.y:172
{ yyVAL.labelNameSlice = model.LabelNames{model.LabelName(yyS[yypt-0].str)} } {
yyVAL.labelNameSlice = clientmodel.LabelNames{clientmodel.LabelName(yyS[yypt-0].str)}
}
case 31: case 31:
//line parser.y:174 //line parser.y:174
{ yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, model.LabelName(yyS[yypt-0].str)) } {
yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, clientmodel.LabelName(yyS[yypt-0].str))
}
case 32: case 32:
//line parser.y:178 //line parser.y:178
{ yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode} } {
yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode}
}
case 33: case 33:
//line parser.y:180 //line parser.y:180
{ yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode) } {
yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode)
}
case 34: case 34:
//line parser.y:184 //line parser.y:184
{ yyVAL.ruleNode = yyS[yypt-0].ruleNode } {
yyVAL.ruleNode = yyS[yypt-0].ruleNode
}
case 35: case 35:
//line parser.y:186 //line parser.y:186
{ yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str) } {
yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str)
}
} }
goto yystack /* stack new state and value */ goto yystack /* stack new state and value */
} }

View file

@ -18,7 +18,8 @@ import (
"html/template" "html/template"
"time" "time"
"github.com/prometheus/prometheus/model" clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/rules/ast" "github.com/prometheus/prometheus/rules/ast"
"github.com/prometheus/prometheus/stats" "github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/storage/metric"
@ -28,7 +29,7 @@ import (
type RecordingRule struct { type RecordingRule struct {
name string name string
vector ast.VectorNode vector ast.VectorNode
labels model.LabelSet labels clientmodel.LabelSet
permanent bool permanent bool
} }
@ -47,7 +48,7 @@ func (rule RecordingRule) Eval(timestamp time.Time, storage *metric.TieredStorag
// Override the metric name and labels. // Override the metric name and labels.
for _, sample := range vector { for _, sample := range vector {
sample.Metric[model.MetricNameLabel] = model.LabelValue(rule.name) sample.Metric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(rule.name)
for label, value := range rule.labels { for label, value := range rule.labels {
if value == "" { if value == "" {
delete(sample.Metric, label) delete(sample.Metric, label)
@ -85,7 +86,7 @@ func (rule RecordingRule) HTMLSnippet() template.HTML {
} }
// Construct a new RecordingRule. // Construct a new RecordingRule.
func NewRecordingRule(name string, labels model.LabelSet, vector ast.VectorNode, permanent bool) *RecordingRule { func NewRecordingRule(name string, labels clientmodel.LabelSet, vector ast.VectorNode, permanent bool) *RecordingRule {
return &RecordingRule{ return &RecordingRule{
name: name, name: name,
labels: labels, labels: labels,

View file

@ -15,15 +15,17 @@ package rules
import ( import (
"fmt" "fmt"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/rules/ast"
"github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/metric"
"github.com/prometheus/prometheus/utility/test"
"path" "path"
"strings" "strings"
"testing" "testing"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/rules/ast"
"github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/metric"
"github.com/prometheus/prometheus/utility/test"
) )
var ( var (
@ -528,7 +530,7 @@ func TestAlertingRule(t *testing.T) {
t.Fatalf("Unable to parse alert expression: %s", err) t.Fatalf("Unable to parse alert expression: %s", err)
} }
alertName := "HttpRequestRateLow" alertName := "HttpRequestRateLow"
alertLabels := model.LabelSet{ alertLabels := clientmodel.LabelSet{
"summary": "HTTP request rate is low", "summary": "HTTP request rate is low",
} }
rule := NewAlertingRule(alertName, alertExpr.(ast.VectorNode), time.Minute, alertLabels) rule := NewAlertingRule(alertName, alertExpr.(ast.VectorNode), time.Minute, alertLabels)

View file

@ -14,16 +14,21 @@
package metric package metric
import ( import (
"code.google.com/p/goprotobuf/proto" "bytes"
"fmt" "fmt"
"github.com/prometheus/prometheus/coding" "strings"
"github.com/prometheus/prometheus/model" "time"
"code.google.com/p/goprotobuf/proto"
clientmodel "github.com/prometheus/client_golang/model"
dto "github.com/prometheus/prometheus/model/generated" dto "github.com/prometheus/prometheus/model/generated"
"github.com/prometheus/prometheus/coding"
"github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/storage/raw" "github.com/prometheus/prometheus/storage/raw"
"github.com/prometheus/prometheus/storage/raw/leveldb" "github.com/prometheus/prometheus/storage/raw/leveldb"
"strings"
"time"
) )
// CurationState contains high-level curation state information for the // CurationState contains high-level curation state information for the
@ -32,28 +37,7 @@ type CurationState struct {
Active bool Active bool
Name string Name string
Limit time.Duration Limit time.Duration
Fingerprint *model.Fingerprint Fingerprint *clientmodel.Fingerprint
}
// watermarkFilter determines whether to include or exclude candidate
// values from the curation process by virtue of how old the high watermark is.
type watermarkFilter struct {
// curationState is the data store for curation remarks.
curationState raw.Persistence
// ignoreYoungerThan conveys this filter's policy of not working on elements
// younger than a given relative time duration. This is persisted to the
// curation remark database (curationState) to indicate how far a given
// policy of this type has progressed.
ignoreYoungerThan time.Duration
// processor is the post-processor that performs whatever action is desired on
// the data that is deemed valid to be worked on.
processor Processor
// stop functions as the global stop channel for all future operations.
stop chan bool
// stopAt is used to determine the elegibility of series for compaction.
stopAt time.Time
// status is the outbound channel for notifying the status page of its state.
status chan CurationState
} }
// curator is responsible for effectuating a given curation policy across the // curator is responsible for effectuating a given curation policy across the
@ -66,17 +50,19 @@ type Curator struct {
Stop chan bool Stop chan bool
} }
// watermarkDecoder converts (dto.Fingerprint, dto.MetricHighWatermark) doubles // watermarkScanner converts (dto.Fingerprint, dto.MetricHighWatermark) doubles
// into (model.Fingerprint, model.Watermark) doubles. // into (model.Fingerprint, model.Watermark) doubles.
type watermarkDecoder struct{} //
// watermarkScanner determines whether to include or exclude candidate
// watermarkOperator scans over the curator.samples table for metrics whose // values from the curation process by virtue of how old the high watermark is.
//
// watermarkScanner scans over the curator.samples table for metrics whose
// high watermark has been determined to be allowable for curation. This type // high watermark has been determined to be allowable for curation. This type
// is individually responsible for compaction. // is individually responsible for compaction.
// //
// The scanning starts from CurationRemark.LastCompletionTimestamp and goes // The scanning starts from CurationRemark.LastCompletionTimestamp and goes
// forward until the stop point or end of the series is reached. // forward until the stop point or end of the series is reached.
type watermarkOperator struct { type watermarkScanner struct {
// curationState is the data store for curation remarks. // curationState is the data store for curation remarks.
curationState raw.Persistence curationState raw.Persistence
// diskFrontier models the available seekable ranges for the provided // diskFrontier models the available seekable ranges for the provided
@ -93,6 +79,11 @@ type watermarkOperator struct {
samples raw.Persistence samples raw.Persistence
// stopAt is a cue for when to stop mutating a given series. // stopAt is a cue for when to stop mutating a given series.
stopAt time.Time stopAt time.Time
// stop functions as the global stop channel for all future operations.
stop chan bool
// status is the outbound channel for notifying the status page of its state.
status chan CurationState
} }
// run facilitates the curation lifecycle. // run facilitates the curation lifecycle.
@ -101,7 +92,7 @@ type watermarkOperator struct {
// curated. // curated.
// curationState is the on-disk store where the curation remarks are made for // curationState is the on-disk store where the curation remarks are made for
// how much progress has been made. // how much progress has been made.
func (c Curator) Run(ignoreYoungerThan time.Duration, instant time.Time, processor Processor, curationState, samples, watermarks *leveldb.LevelDBPersistence, status chan CurationState) (err error) { func (c *Curator) Run(ignoreYoungerThan time.Duration, instant time.Time, processor Processor, curationState, samples, watermarks *leveldb.LevelDBPersistence, status chan CurationState) (err error) {
defer func(t time.Time) { defer func(t time.Time) {
duration := float64(time.Since(t) / time.Millisecond) duration := float64(time.Since(t) / time.Millisecond)
@ -137,104 +128,89 @@ func (c Curator) Run(ignoreYoungerThan time.Duration, instant time.Time, process
return return
} }
decoder := watermarkDecoder{} scanner := &watermarkScanner{
filter := watermarkFilter{
curationState: curationState, curationState: curationState,
ignoreYoungerThan: ignoreYoungerThan, ignoreYoungerThan: ignoreYoungerThan,
processor: processor, processor: processor,
status: status, status: status,
stop: c.Stop, stop: c.Stop,
stopAt: instant.Add(-1 * ignoreYoungerThan), stopAt: instant.Add(-1 * ignoreYoungerThan),
diskFrontier: diskFrontier,
sampleIterator: iterator,
samples: samples,
} }
// Right now, the ability to stop a curation is limited to the beginning of // Right now, the ability to stop a curation is limited to the beginning of
// each fingerprint cycle. It is impractical to cease the work once it has // each fingerprint cycle. It is impractical to cease the work once it has
// begun for a given series. // begun for a given series.
operator := watermarkOperator{ _, err = watermarks.ForEach(scanner, scanner, scanner)
curationState: curationState,
diskFrontier: diskFrontier,
processor: processor,
ignoreYoungerThan: ignoreYoungerThan,
sampleIterator: iterator,
samples: samples,
stopAt: instant.Add(-1 * ignoreYoungerThan),
}
_, err = watermarks.ForEach(decoder, filter, operator)
return return
} }
// drain instructs the curator to stop at the next convenient moment as to not // drain instructs the curator to stop at the next convenient moment as to not
// introduce data inconsistencies. // introduce data inconsistencies.
func (c Curator) Drain() { func (c *Curator) Drain() {
if len(c.Stop) == 0 { if len(c.Stop) == 0 {
c.Stop <- true c.Stop <- true
} }
} }
func (w watermarkDecoder) DecodeKey(in interface{}) (out interface{}, err error) { func (w *watermarkScanner) DecodeKey(in interface{}) (interface{}, error) {
key := &dto.Fingerprint{} key := new(dto.Fingerprint)
bytes := in.([]byte) bytes := in.([]byte)
err = proto.Unmarshal(bytes, key) if err := proto.Unmarshal(bytes, key); err != nil {
if err != nil { return nil, err
return
} }
out = model.NewFingerprintFromDTO(key) fingerprint := new(clientmodel.Fingerprint)
loadFingerprint(fingerprint, key)
return return fingerprint, nil
} }
func (w watermarkDecoder) DecodeValue(in interface{}) (out interface{}, err error) { func (w *watermarkScanner) DecodeValue(in interface{}) (interface{}, error) {
dto := &dto.MetricHighWatermark{} value := new(dto.MetricHighWatermark)
bytes := in.([]byte) bytes := in.([]byte)
err = proto.Unmarshal(bytes, dto) if err := proto.Unmarshal(bytes, value); err != nil {
if err != nil { return nil, err
return
} }
out = model.NewWatermarkFromHighWatermarkDTO(dto) watermark := new(watermarks)
watermark.load(value)
return return watermark, nil
} }
func (w watermarkFilter) shouldStop() bool { func (w *watermarkScanner) shouldStop() bool {
return len(w.stop) != 0 return len(w.stop) != 0
} }
func getCurationRemark(states raw.Persistence, processor Processor, ignoreYoungerThan time.Duration, fingerprint *model.Fingerprint) (*model.CurationRemark, error) { func (w *watermarkScanner) getCurationRemark(k *curationKey) (r *curationRemark, found bool, err error) {
rawSignature, err := processor.Signature() curationKey := new(dto.CurationKey)
if err != nil { curationValue := new(dto.CurationValue)
return nil, err
}
curationKey := model.CurationKey{ k.dump(curationKey)
Fingerprint: fingerprint,
ProcessorMessageRaw: rawSignature,
ProcessorMessageTypeName: processor.Name(),
IgnoreYoungerThan: ignoreYoungerThan,
}.ToDTO()
curationValue := &dto.CurationValue{}
present, err := states.Get(curationKey, curationValue) present, err := w.curationState.Get(curationKey, curationValue)
if err != nil { if err != nil {
return nil, err return nil, false, err
} }
if !present { if !present {
return nil, nil return nil, false, nil
} }
remark := model.NewCurationRemarkFromDTO(curationValue) remark := new(curationRemark)
remark.load(curationValue)
return &remark, nil return remark, true, nil
} }
func (w watermarkFilter) Filter(key, value interface{}) (r storage.FilterResult) { func (w *watermarkScanner) Filter(key, value interface{}) (r storage.FilterResult) {
fingerprint := key.(*model.Fingerprint) fingerprint := key.(*clientmodel.Fingerprint)
defer func() { defer func() {
labels := map[string]string{ labels := map[string]string{
@ -244,9 +220,7 @@ func (w watermarkFilter) Filter(key, value interface{}) (r storage.FilterResult)
} }
curationFilterOperations.Increment(labels) curationFilterOperations.Increment(labels)
}()
defer func() {
select { select {
case w.status <- CurationState{ case w.status <- CurationState{
Active: true, Active: true,
@ -263,19 +237,25 @@ func (w watermarkFilter) Filter(key, value interface{}) (r storage.FilterResult)
return storage.STOP return storage.STOP
} }
curationRemark, err := getCurationRemark(w.curationState, w.processor, w.ignoreYoungerThan, fingerprint) k := &curationKey{
Fingerprint: fingerprint,
ProcessorMessageRaw: w.processor.Signature(),
ProcessorMessageTypeName: w.processor.Name(),
IgnoreYoungerThan: w.ignoreYoungerThan,
}
curationRemark, present, err := w.getCurationRemark(k)
if err != nil { if err != nil {
return return
} }
if curationRemark == nil { if !present {
r = storage.ACCEPT return storage.ACCEPT
return
} }
if !curationRemark.OlderThan(w.stopAt) { if !curationRemark.OlderThan(w.stopAt) {
return storage.SKIP return storage.SKIP
} }
watermark := value.(model.Watermark) watermark := value.(*watermarks)
if !curationRemark.OlderThan(watermark.Time) { if !curationRemark.OlderThan(watermark.High) {
return storage.SKIP return storage.SKIP
} }
curationConsistent, err := w.curationConsistent(fingerprint, watermark) curationConsistent, err := w.curationConsistent(fingerprint, watermark)
@ -291,20 +271,29 @@ func (w watermarkFilter) Filter(key, value interface{}) (r storage.FilterResult)
// curationConsistent determines whether the given metric is in a dirty state // curationConsistent determines whether the given metric is in a dirty state
// and needs curation. // and needs curation.
func (w watermarkFilter) curationConsistent(f *model.Fingerprint, watermark model.Watermark) (consistent bool, err error) { func (w *watermarkScanner) curationConsistent(f *clientmodel.Fingerprint, watermark *watermarks) (bool, error) {
curationRemark, err := getCurationRemark(w.curationState, w.processor, w.ignoreYoungerThan, f) k := &curationKey{
if err != nil { Fingerprint: f,
return ProcessorMessageRaw: w.processor.Signature(),
ProcessorMessageTypeName: w.processor.Name(),
IgnoreYoungerThan: w.ignoreYoungerThan,
} }
if !curationRemark.OlderThan(watermark.Time) { curationRemark, present, err := w.getCurationRemark(k)
consistent = true if err != nil {
return false, err
}
if !present {
return false, nil
}
if !curationRemark.OlderThan(watermark.High) {
return true, nil
} }
return return false, nil
} }
func (w watermarkOperator) Operate(key, _ interface{}) (oErr *storage.OperatorError) { func (w *watermarkScanner) Operate(key, _ interface{}) (oErr *storage.OperatorError) {
fingerprint := key.(*model.Fingerprint) fingerprint := key.(*clientmodel.Fingerprint)
seriesFrontier, present, err := newSeriesFrontier(fingerprint, w.diskFrontier, w.sampleIterator) seriesFrontier, present, err := newSeriesFrontier(fingerprint, w.diskFrontier, w.sampleIterator)
if err != nil || !present { if err != nil || !present {
@ -314,7 +303,14 @@ func (w watermarkOperator) Operate(key, _ interface{}) (oErr *storage.OperatorEr
return &storage.OperatorError{error: err, Continuable: false} return &storage.OperatorError{error: err, Continuable: false}
} }
curationState, err := getCurationRemark(w.curationState, w.processor, w.ignoreYoungerThan, fingerprint) k := &curationKey{
Fingerprint: fingerprint,
ProcessorMessageRaw: w.processor.Signature(),
ProcessorMessageTypeName: w.processor.Name(),
IgnoreYoungerThan: w.ignoreYoungerThan,
}
curationState, _, err := w.getCurationRemark(k)
if err != nil { if err != nil {
// An anomaly with the curation remark is likely not fatal in the sense that // An anomaly with the curation remark is likely not fatal in the sense that
// there was a decoding error with the entity and shouldn't be cause to stop // there was a decoding error with the entity and shouldn't be cause to stop
@ -323,12 +319,14 @@ func (w watermarkOperator) Operate(key, _ interface{}) (oErr *storage.OperatorEr
return &storage.OperatorError{error: err, Continuable: true} return &storage.OperatorError{error: err, Continuable: true}
} }
startKey := model.SampleKey{ startKey := &SampleKey{
Fingerprint: fingerprint, Fingerprint: fingerprint,
FirstTimestamp: seriesFrontier.optimalStartTime(curationState), FirstTimestamp: seriesFrontier.optimalStartTime(curationState),
} }
dto := new(dto.SampleKey)
prospectiveKey := coding.NewPBEncoder(startKey.ToDTO()).MustEncode() startKey.Dump(dto)
prospectiveKey := coding.NewPBEncoder(dto).MustEncode()
if !w.sampleIterator.Seek(prospectiveKey) { if !w.sampleIterator.Seek(prospectiveKey) {
// LevelDB is picky about the seek ranges. If an iterator was invalidated, // LevelDB is picky about the seek ranges. If an iterator was invalidated,
// no work may occur, and the iterator cannot be recovered. // no work may occur, and the iterator cannot be recovered.
@ -358,22 +356,101 @@ func (w watermarkOperator) Operate(key, _ interface{}) (oErr *storage.OperatorEr
return return
} }
func (w watermarkOperator) refreshCurationRemark(f *model.Fingerprint, finished time.Time) (err error) { func (w *watermarkScanner) refreshCurationRemark(f *clientmodel.Fingerprint, finished time.Time) error {
signature, err := w.processor.Signature() curationKey := curationKey{
if err != nil {
return
}
curationKey := model.CurationKey{
Fingerprint: f, Fingerprint: f,
ProcessorMessageRaw: signature, ProcessorMessageRaw: w.processor.Signature(),
ProcessorMessageTypeName: w.processor.Name(), ProcessorMessageTypeName: w.processor.Name(),
IgnoreYoungerThan: w.ignoreYoungerThan, IgnoreYoungerThan: w.ignoreYoungerThan,
}.ToDTO() }
curationValue := model.CurationRemark{ k := new(dto.CurationKey)
curationKey.dump(k)
curationValue := curationRemark{
LastCompletionTimestamp: finished, LastCompletionTimestamp: finished,
}.ToDTO() }
v := new(dto.CurationValue)
curationValue.dump(v)
err = w.curationState.Put(curationKey, curationValue) return w.curationState.Put(k, v)
}
return
// curationRemark provides a representation of dto.CurationValue with associated
// business logic methods attached to it to enhance code readability.
type curationRemark struct {
LastCompletionTimestamp time.Time
}
// OlderThan answers whether this curationRemark is older than the provided
// cutOff time.
func (c *curationRemark) OlderThan(t time.Time) bool {
return c.LastCompletionTimestamp.Before(t)
}
// Equal answers whether the two curationRemarks are equivalent.
func (c *curationRemark) Equal(o curationRemark) bool {
return c.LastCompletionTimestamp.Equal(o.LastCompletionTimestamp)
}
func (c *curationRemark) String() string {
return fmt.Sprintf("Last curated at %s", c.LastCompletionTimestamp)
}
func (c *curationRemark) load(d *dto.CurationValue) {
c.LastCompletionTimestamp = time.Unix(d.GetLastCompletionTimestamp(), 0).UTC()
}
func (c *curationRemark) dump(d *dto.CurationValue) {
d.Reset()
d.LastCompletionTimestamp = proto.Int64(c.LastCompletionTimestamp.Unix())
}
// curationKey provides a representation of dto.CurationKey with associated
// business logic methods attached to it to enhance code readability.
type curationKey struct {
Fingerprint *clientmodel.Fingerprint
ProcessorMessageRaw []byte
ProcessorMessageTypeName string
IgnoreYoungerThan time.Duration
}
// Equal answers whether the two curationKeys are equivalent.
func (c *curationKey) Equal(o *curationKey) bool {
switch {
case !c.Fingerprint.Equal(o.Fingerprint):
return false
case bytes.Compare(c.ProcessorMessageRaw, o.ProcessorMessageRaw) != 0:
return false
case c.ProcessorMessageTypeName != o.ProcessorMessageTypeName:
return false
case c.IgnoreYoungerThan != o.IgnoreYoungerThan:
return false
}
return true
}
func (c *curationKey) dump(d *dto.CurationKey) {
d.Reset()
// BUG(matt): Avenue for simplification.
fingerprintDTO := &dto.Fingerprint{}
dumpFingerprint(fingerprintDTO, c.Fingerprint)
d.Fingerprint = fingerprintDTO
d.ProcessorMessageRaw = c.ProcessorMessageRaw
d.ProcessorMessageTypeName = proto.String(c.ProcessorMessageTypeName)
d.IgnoreYoungerThan = proto.Int64(int64(c.IgnoreYoungerThan))
}
func (c *curationKey) load(d *dto.CurationKey) {
// BUG(matt): Avenue for simplification.
c.Fingerprint = &clientmodel.Fingerprint{}
loadFingerprint(c.Fingerprint, d.Fingerprint)
c.ProcessorMessageRaw = d.ProcessorMessageRaw
c.ProcessorMessageTypeName = d.GetProcessorMessageTypeName()
c.IgnoreYoungerThan = time.Duration(d.GetIgnoreYoungerThan())
} }

68
storage/metric/dto.go Normal file
View file

@ -0,0 +1,68 @@
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metric
import (
"sort"
"code.google.com/p/goprotobuf/proto"
clientmodel "github.com/prometheus/client_golang/model"
dto "github.com/prometheus/prometheus/model/generated"
)
func dumpFingerprint(d *dto.Fingerprint, f *clientmodel.Fingerprint) {
d.Reset()
d.Signature = proto.String(f.String())
}
func loadFingerprint(f *clientmodel.Fingerprint, d *dto.Fingerprint) {
f.LoadFromString(d.GetSignature())
}
func dumpMetric(d *dto.Metric, m clientmodel.Metric) {
d.Reset()
metricLength := len(m)
labelNames := make([]string, 0, metricLength)
for labelName := range m {
labelNames = append(labelNames, string(labelName))
}
sort.Strings(labelNames)
pairs := make([]*dto.LabelPair, 0, metricLength)
for _, labelName := range labelNames {
l := clientmodel.LabelName(labelName)
labelValue := m[l]
labelPair := &dto.LabelPair{
Name: proto.String(string(labelName)),
Value: proto.String(string(labelValue)),
}
pairs = append(pairs, labelPair)
}
d.LabelPair = pairs
}
func dumpLabelName(d *dto.LabelName, l clientmodel.LabelName) {
d.Reset()
d.Name = proto.String(string(l))
}

View file

@ -14,33 +14,35 @@
package metric package metric
import ( import (
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/utility/test"
"testing" "testing"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/utility/test"
) )
func GetFingerprintsForLabelSetTests(p MetricPersistence, t test.Tester) { func GetFingerprintsForLabelSetTests(p MetricPersistence, t test.Tester) {
testAppendSample(p, model.Sample{ testAppendSample(p, &clientmodel.Sample{
Value: 0, Value: 0,
Timestamp: time.Time{}, Timestamp: time.Time{},
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "my_metric", clientmodel.MetricNameLabel: "my_metric",
"request_type": "your_mom", "request_type": "your_mom",
}, },
}, t) }, t)
testAppendSample(p, model.Sample{ testAppendSample(p, &clientmodel.Sample{
Value: 0, Value: 0,
Timestamp: time.Time{}, Timestamp: time.Time{},
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "my_metric", clientmodel.MetricNameLabel: "my_metric",
"request_type": "your_dad", "request_type": "your_dad",
}, },
}, t) }, t)
result, err := p.GetFingerprintsForLabelSet(model.LabelSet{ result, err := p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
model.MetricNameLabel: model.LabelValue("my_metric"), clientmodel.MetricNameLabel: clientmodel.LabelValue("my_metric"),
}) })
if err != nil { if err != nil {
@ -51,8 +53,8 @@ func GetFingerprintsForLabelSetTests(p MetricPersistence, t test.Tester) {
t.Errorf("Expected two elements.") t.Errorf("Expected two elements.")
} }
result, err = p.GetFingerprintsForLabelSet(model.LabelSet{ result, err = p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
model.LabelName("request_type"): model.LabelValue("your_mom"), clientmodel.LabelName("request_type"): clientmodel.LabelValue("your_mom"),
}) })
if err != nil { if err != nil {
@ -63,8 +65,8 @@ func GetFingerprintsForLabelSetTests(p MetricPersistence, t test.Tester) {
t.Errorf("Expected one element.") t.Errorf("Expected one element.")
} }
result, err = p.GetFingerprintsForLabelSet(model.LabelSet{ result, err = p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
model.LabelName("request_type"): model.LabelValue("your_dad"), clientmodel.LabelName("request_type"): clientmodel.LabelValue("your_dad"),
}) })
if err != nil { if err != nil {
@ -77,27 +79,27 @@ func GetFingerprintsForLabelSetTests(p MetricPersistence, t test.Tester) {
} }
func GetFingerprintsForLabelNameTests(p MetricPersistence, t test.Tester) { func GetFingerprintsForLabelNameTests(p MetricPersistence, t test.Tester) {
testAppendSample(p, model.Sample{ testAppendSample(p, &clientmodel.Sample{
Value: 0, Value: 0,
Timestamp: time.Time{}, Timestamp: time.Time{},
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "my_metric", clientmodel.MetricNameLabel: "my_metric",
"request_type": "your_mom", "request_type": "your_mom",
"language": "english", "language": "english",
}, },
}, t) }, t)
testAppendSample(p, model.Sample{ testAppendSample(p, &clientmodel.Sample{
Value: 0, Value: 0,
Timestamp: time.Time{}, Timestamp: time.Time{},
Metric: model.Metric{ Metric: clientmodel.Metric{
model.MetricNameLabel: "my_metric", clientmodel.MetricNameLabel: "my_metric",
"request_type": "your_dad", "request_type": "your_dad",
"sprache": "deutsch", "sprache": "deutsch",
}, },
}, t) }, t)
b := model.MetricNameLabel b := clientmodel.MetricNameLabel
result, err := p.GetFingerprintsForLabelName(b) result, err := p.GetFingerprintsForLabelName(b)
if err != nil { if err != nil {
@ -108,7 +110,7 @@ func GetFingerprintsForLabelNameTests(p MetricPersistence, t test.Tester) {
t.Errorf("Expected two elements.") t.Errorf("Expected two elements.")
} }
b = model.LabelName("request_type") b = clientmodel.LabelName("request_type")
result, err = p.GetFingerprintsForLabelName(b) result, err = p.GetFingerprintsForLabelName(b)
if err != nil { if err != nil {
@ -119,7 +121,7 @@ func GetFingerprintsForLabelNameTests(p MetricPersistence, t test.Tester) {
t.Errorf("Expected two elements.") t.Errorf("Expected two elements.")
} }
b = model.LabelName("language") b = clientmodel.LabelName("language")
result, err = p.GetFingerprintsForLabelName(b) result, err = p.GetFingerprintsForLabelName(b)
if err != nil { if err != nil {
@ -130,7 +132,7 @@ func GetFingerprintsForLabelNameTests(p MetricPersistence, t test.Tester) {
t.Errorf("Expected one element.") t.Errorf("Expected one element.")
} }
b = model.LabelName("sprache") b = clientmodel.LabelName("sprache")
result, err = p.GetFingerprintsForLabelName(b) result, err = p.GetFingerprintsForLabelName(b)
if err != nil { if err != nil {
@ -143,25 +145,25 @@ func GetFingerprintsForLabelNameTests(p MetricPersistence, t test.Tester) {
} }
func GetMetricForFingerprintTests(p MetricPersistence, t test.Tester) { func GetMetricForFingerprintTests(p MetricPersistence, t test.Tester) {
testAppendSample(p, model.Sample{ testAppendSample(p, &clientmodel.Sample{
Value: 0, Value: 0,
Timestamp: time.Time{}, Timestamp: time.Time{},
Metric: model.Metric{ Metric: clientmodel.Metric{
"request_type": "your_mom", "request_type": "your_mom",
}, },
}, t) }, t)
testAppendSample(p, model.Sample{ testAppendSample(p, &clientmodel.Sample{
Value: 0, Value: 0,
Timestamp: time.Time{}, Timestamp: time.Time{},
Metric: model.Metric{ Metric: clientmodel.Metric{
"request_type": "your_dad", "request_type": "your_dad",
"one-off": "value", "one-off": "value",
}, },
}, t) }, t)
result, err := p.GetFingerprintsForLabelSet(model.LabelSet{ result, err := p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
model.LabelName("request_type"): model.LabelValue("your_mom"), clientmodel.LabelName("request_type"): clientmodel.LabelValue("your_mom"),
}) })
if err != nil { if err != nil {
@ -189,8 +191,8 @@ func GetMetricForFingerprintTests(p MetricPersistence, t test.Tester) {
t.Errorf("Expected metric to match.") t.Errorf("Expected metric to match.")
} }
result, err = p.GetFingerprintsForLabelSet(model.LabelSet{ result, err = p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
model.LabelName("request_type"): model.LabelValue("your_dad"), clientmodel.LabelName("request_type"): clientmodel.LabelValue("your_dad"),
}) })
if err != nil { if err != nil {
@ -250,10 +252,10 @@ func GetMetricForFingerprintTests(p MetricPersistence, t test.Tester) {
} }
func AppendRepeatingValuesTests(p MetricPersistence, t test.Tester) { func AppendRepeatingValuesTests(p MetricPersistence, t test.Tester) {
metric := model.Metric{ metric := clientmodel.Metric{
model.MetricNameLabel: "errors_total", clientmodel.MetricNameLabel: "errors_total",
"controller": "foo", "controller": "foo",
"operation": "bar", "operation": "bar",
} }
increments := 10 increments := 10
@ -262,8 +264,8 @@ func AppendRepeatingValuesTests(p MetricPersistence, t test.Tester) {
for i := 0; i < increments; i++ { for i := 0; i < increments; i++ {
for j := 0; j < repetitions; j++ { for j := 0; j < repetitions; j++ {
time := time.Time{}.Add(time.Duration(i) * time.Hour).Add(time.Duration(j) * time.Second) time := time.Time{}.Add(time.Duration(i) * time.Hour).Add(time.Duration(j) * time.Second)
testAppendSample(p, model.Sample{ testAppendSample(p, &clientmodel.Sample{
Value: model.SampleValue(i), Value: clientmodel.SampleValue(i),
Timestamp: time, Timestamp: time,
Metric: metric, Metric: metric,
}, t) }, t)
@ -275,10 +277,10 @@ func AppendRepeatingValuesTests(p MetricPersistence, t test.Tester) {
return return
} }
labelSet := model.LabelSet{ labelSet := clientmodel.LabelSet{
model.MetricNameLabel: "errors_total", clientmodel.MetricNameLabel: "errors_total",
"controller": "foo", "controller": "foo",
"operation": "bar", "operation": "bar",
} }
for i := 0; i < increments; i++ { for i := 0; i < increments; i++ {
@ -297,7 +299,7 @@ func AppendRepeatingValuesTests(p MetricPersistence, t test.Tester) {
t.Fatal("expected at least one sample.") t.Fatal("expected at least one sample.")
} }
expected := model.SampleValue(i) expected := clientmodel.SampleValue(i)
for _, sample := range samples { for _, sample := range samples {
if sample.Value != expected { if sample.Value != expected {
@ -309,21 +311,21 @@ func AppendRepeatingValuesTests(p MetricPersistence, t test.Tester) {
} }
func AppendsRepeatingValuesTests(p MetricPersistence, t test.Tester) { func AppendsRepeatingValuesTests(p MetricPersistence, t test.Tester) {
metric := model.Metric{ metric := clientmodel.Metric{
model.MetricNameLabel: "errors_total", clientmodel.MetricNameLabel: "errors_total",
"controller": "foo", "controller": "foo",
"operation": "bar", "operation": "bar",
} }
increments := 10 increments := 10
repetitions := 500 repetitions := 500
s := model.Samples{} s := clientmodel.Samples{}
for i := 0; i < increments; i++ { for i := 0; i < increments; i++ {
for j := 0; j < repetitions; j++ { for j := 0; j < repetitions; j++ {
time := time.Time{}.Add(time.Duration(i) * time.Hour).Add(time.Duration(j) * time.Second) time := time.Time{}.Add(time.Duration(i) * time.Hour).Add(time.Duration(j) * time.Second)
s = append(s, model.Sample{ s = append(s, &clientmodel.Sample{
Value: model.SampleValue(i), Value: clientmodel.SampleValue(i),
Timestamp: time, Timestamp: time,
Metric: metric, Metric: metric,
}) })
@ -337,10 +339,10 @@ func AppendsRepeatingValuesTests(p MetricPersistence, t test.Tester) {
return return
} }
labelSet := model.LabelSet{ labelSet := clientmodel.LabelSet{
model.MetricNameLabel: "errors_total", clientmodel.MetricNameLabel: "errors_total",
"controller": "foo", "controller": "foo",
"operation": "bar", "operation": "bar",
} }
for i := 0; i < increments; i++ { for i := 0; i < increments; i++ {
@ -359,7 +361,7 @@ func AppendsRepeatingValuesTests(p MetricPersistence, t test.Tester) {
t.Fatal("expected at least one sample.") t.Fatal("expected at least one sample.")
} }
expected := model.SampleValue(i) expected := clientmodel.SampleValue(i)
for _, sample := range samples { for _, sample := range samples {
if sample.Value != expected { if sample.Value != expected {

View file

@ -15,12 +15,15 @@ package metric
import ( import (
"fmt" "fmt"
"time"
clientmodel "github.com/prometheus/client_golang/model"
dto "github.com/prometheus/prometheus/model/generated"
"github.com/prometheus/prometheus/coding" "github.com/prometheus/prometheus/coding"
"github.com/prometheus/prometheus/coding/indexable" "github.com/prometheus/prometheus/coding/indexable"
"github.com/prometheus/prometheus/model"
dto "github.com/prometheus/prometheus/model/generated"
"github.com/prometheus/prometheus/storage/raw/leveldb" "github.com/prometheus/prometheus/storage/raw/leveldb"
"time"
) )
// diskFrontier describes an on-disk store of series to provide a // diskFrontier describes an on-disk store of series to provide a
@ -29,17 +32,17 @@ import (
// This is used to reduce the burden associated with LevelDB iterator // This is used to reduce the burden associated with LevelDB iterator
// management. // management.
type diskFrontier struct { type diskFrontier struct {
firstFingerprint *model.Fingerprint firstFingerprint *clientmodel.Fingerprint
firstSupertime time.Time firstSupertime time.Time
lastFingerprint *model.Fingerprint lastFingerprint *clientmodel.Fingerprint
lastSupertime time.Time lastSupertime time.Time
} }
func (f diskFrontier) String() string { func (f diskFrontier) String() string {
return fmt.Sprintf("diskFrontier from %s at %s to %s at %s", f.firstFingerprint.ToRowKey(), f.firstSupertime, f.lastFingerprint.ToRowKey(), f.lastSupertime) return fmt.Sprintf("diskFrontier from %s at %s to %s at %s", f.firstFingerprint, f.firstSupertime, f.lastFingerprint, f.lastSupertime)
} }
func (f diskFrontier) ContainsFingerprint(fingerprint *model.Fingerprint) bool { func (f *diskFrontier) ContainsFingerprint(fingerprint *clientmodel.Fingerprint) bool {
return !(fingerprint.Less(f.firstFingerprint) || f.lastFingerprint.Less(fingerprint)) return !(fingerprint.Less(f.firstFingerprint) || f.lastFingerprint.Less(fingerprint))
} }
@ -60,14 +63,12 @@ func newDiskFrontier(i leveldb.Iterator) (d *diskFrontier, present bool, err err
return nil, false, err return nil, false, err
} }
d = &diskFrontier{} return &diskFrontier{
firstFingerprint: firstKey.Fingerprint,
d.firstFingerprint = firstKey.Fingerprint firstSupertime: firstKey.FirstTimestamp,
d.firstSupertime = firstKey.FirstTimestamp lastFingerprint: lastKey.Fingerprint,
d.lastFingerprint = lastKey.Fingerprint lastSupertime: lastKey.FirstTimestamp,
d.lastSupertime = lastKey.FirstTimestamp }, true, nil
return d, true, nil
} }
// seriesFrontier represents the valid seek frontier for a given series. // seriesFrontier represents the valid seek frontier for a given series.
@ -77,13 +78,13 @@ type seriesFrontier struct {
lastTime time.Time lastTime time.Time
} }
func (f seriesFrontier) String() string { func (f *seriesFrontier) String() string {
return fmt.Sprintf("seriesFrontier from %s to %s at %s", f.firstSupertime, f.lastSupertime, f.lastTime) return fmt.Sprintf("seriesFrontier from %s to %s at %s", f.firstSupertime, f.lastSupertime, f.lastTime)
} }
// newSeriesFrontier furnishes a populated diskFrontier for a given // newSeriesFrontier furnishes a populated diskFrontier for a given
// fingerprint. If the series is absent, present will be false. // fingerprint. If the series is absent, present will be false.
func newSeriesFrontier(f *model.Fingerprint, d *diskFrontier, i leveldb.Iterator) (s *seriesFrontier, present bool, err error) { func newSeriesFrontier(f *clientmodel.Fingerprint, d *diskFrontier, i leveldb.Iterator) (s *seriesFrontier, present bool, err error) {
lowerSeek := firstSupertime lowerSeek := firstSupertime
upperSeek := lastSupertime upperSeek := lastSupertime
@ -104,8 +105,10 @@ func newSeriesFrontier(f *model.Fingerprint, d *diskFrontier, i leveldb.Iterator
} }
// TODO: Convert this to SampleKey.ToPartialDTO. // TODO: Convert this to SampleKey.ToPartialDTO.
fp := new(dto.Fingerprint)
dumpFingerprint(fp, f)
key := &dto.SampleKey{ key := &dto.SampleKey{
Fingerprint: f.ToDTO(), Fingerprint: fp,
Timestamp: upperSeek, Timestamp: upperSeek,
} }
@ -169,14 +172,14 @@ func newSeriesFrontier(f *model.Fingerprint, d *diskFrontier, i leveldb.Iterator
// Contains indicates whether a given time value is within the recorded // Contains indicates whether a given time value is within the recorded
// interval. // interval.
func (s seriesFrontier) Contains(t time.Time) bool { func (s *seriesFrontier) Contains(t time.Time) bool {
return !(t.Before(s.firstSupertime) || t.After(s.lastTime)) return !(t.Before(s.firstSupertime) || t.After(s.lastTime))
} }
// InSafeSeekRange indicates whether the time is within the recorded time range // InSafeSeekRange indicates whether the time is within the recorded time range
// and is safely seekable such that a seek does not result in an iterator point // and is safely seekable such that a seek does not result in an iterator point
// after the last value of the series or outside of the entire store. // after the last value of the series or outside of the entire store.
func (s seriesFrontier) InSafeSeekRange(t time.Time) (safe bool) { func (s *seriesFrontier) InSafeSeekRange(t time.Time) (safe bool) {
if !s.Contains(t) { if !s.Contains(t) {
return return
} }
@ -188,13 +191,13 @@ func (s seriesFrontier) InSafeSeekRange(t time.Time) (safe bool) {
return true return true
} }
func (s seriesFrontier) After(t time.Time) bool { func (s *seriesFrontier) After(t time.Time) bool {
return s.firstSupertime.After(t) return s.firstSupertime.After(t)
} }
// optimalStartTime indicates what the best start time for a curation operation // optimalStartTime indicates what the best start time for a curation operation
// should be given the curation remark. // should be given the curation remark.
func (s seriesFrontier) optimalStartTime(remark *model.CurationRemark) (t time.Time) { func (s *seriesFrontier) optimalStartTime(remark *curationRemark) (t time.Time) {
switch { switch {
case remark == nil: case remark == nil:
t = s.firstSupertime t = s.firstSupertime

View file

@ -15,9 +15,11 @@ package metric
import ( import (
"fmt" "fmt"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/utility/test"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/utility/test"
) )
var ( var (
@ -26,7 +28,7 @@ var (
testInstant = time.Date(1972, 7, 18, 19, 5, 45, 0, usEastern).In(time.UTC) testInstant = time.Date(1972, 7, 18, 19, 5, 45, 0, usEastern).In(time.UTC)
) )
func testAppendSample(p MetricPersistence, s model.Sample, t test.Tester) { func testAppendSample(p MetricPersistence, s *clientmodel.Sample, t test.Tester) {
err := p.AppendSample(s) err := p.AppendSample(s)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -48,10 +50,12 @@ func buildLevelDBTestPersistencesMaker(name string, t test.Tester) func() (Metri
func buildLevelDBTestPersistence(name string, f func(p MetricPersistence, t test.Tester)) func(t test.Tester) { func buildLevelDBTestPersistence(name string, f func(p MetricPersistence, t test.Tester)) func(t test.Tester) {
return func(t test.Tester) { return func(t test.Tester) {
temporaryDirectory := test.NewTemporaryDirectory(fmt.Sprintf("test_leveldb_%s", name), t) temporaryDirectory := test.NewTemporaryDirectory(fmt.Sprintf("test_leveldb_%s", name), t)
defer temporaryDirectory.Close() defer temporaryDirectory.Close()
p, err := NewLevelDBMetricPersistence(temporaryDirectory.Path()) p, err := NewLevelDBMetricPersistence(temporaryDirectory.Path())
if err != nil { if err != nil {
t.Errorf("Could not create LevelDB Metric Persistence: %q\n", err) t.Errorf("Could not create LevelDB Metric Persistence: %q\n", err)
} }
@ -78,12 +82,12 @@ type testTieredStorageCloser struct {
directory test.Closer directory test.Closer
} }
func (t testTieredStorageCloser) Close() { func (t *testTieredStorageCloser) Close() {
t.storage.Close() t.storage.Close()
t.directory.Close() t.directory.Close()
} }
func NewTestTieredStorage(t test.Tester) (storage *TieredStorage, closer test.Closer) { func NewTestTieredStorage(t test.Tester) (*TieredStorage, test.Closer) {
var directory test.TemporaryDirectory var directory test.TemporaryDirectory
directory = test.NewTemporaryDirectory("test_tiered_storage", t) directory = test.NewTemporaryDirectory("test_tiered_storage", t)
storage, err := NewTieredStorage(2500, 1000, 5*time.Second, 0, directory.Path()) storage, err := NewTieredStorage(2500, 1000, 5*time.Second, 0, directory.Path())
@ -105,9 +109,10 @@ func NewTestTieredStorage(t test.Tester) (storage *TieredStorage, closer test.Cl
go storage.Serve(started) go storage.Serve(started)
<-started <-started
closer = &testTieredStorageCloser{ closer := &testTieredStorageCloser{
storage: storage, storage: storage,
directory: directory, directory: directory,
} }
return
return storage, closer
} }

View file

@ -14,9 +14,11 @@
package metric package metric
import ( import (
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/storage"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/storage"
) )
// MetricPersistence is a system for storing metric samples in a persistence // MetricPersistence is a system for storing metric samples in a persistence
@ -31,31 +33,31 @@ type MetricPersistence interface {
// Flush() error // Flush() error
// Record a new sample in the storage layer. // Record a new sample in the storage layer.
AppendSample(model.Sample) error AppendSample(*clientmodel.Sample) error
// Record a group of new samples in the storage layer. // Record a group of new samples in the storage layer.
AppendSamples(model.Samples) error AppendSamples(clientmodel.Samples) error
// Get all of the metric fingerprints that are associated with the provided // Get all of the metric fingerprints that are associated with the provided
// label set. // label set.
GetFingerprintsForLabelSet(model.LabelSet) (model.Fingerprints, error) GetFingerprintsForLabelSet(clientmodel.LabelSet) (clientmodel.Fingerprints, error)
// Get all of the metric fingerprints that are associated for a given label // Get all of the metric fingerprints that are associated for a given label
// name. // name.
GetFingerprintsForLabelName(model.LabelName) (model.Fingerprints, error) GetFingerprintsForLabelName(clientmodel.LabelName) (clientmodel.Fingerprints, error)
// Get the metric associated with the provided fingerprint. // Get the metric associated with the provided fingerprint.
GetMetricForFingerprint(*model.Fingerprint) (model.Metric, error) GetMetricForFingerprint(*clientmodel.Fingerprint) (clientmodel.Metric, error)
// Get the two metric values that are immediately adjacent to a given time. // Get the two metric values that are immediately adjacent to a given time.
GetValueAtTime(*model.Fingerprint, time.Time) model.Values GetValueAtTime(*clientmodel.Fingerprint, time.Time) Values
// Get the boundary values of an interval: the first value older than the // Get the boundary values of an interval: the first value older than the
// interval start, and the first value younger than the interval end. // interval start, and the first value younger than the interval end.
GetBoundaryValues(*model.Fingerprint, model.Interval) model.Values GetBoundaryValues(*clientmodel.Fingerprint, Interval) Values
// Get all values contained within a provided interval. // Get all values contained within a provided interval.
GetRangeValues(*model.Fingerprint, model.Interval) model.Values GetRangeValues(*clientmodel.Fingerprint, Interval) Values
// Get all label values that are associated with a given label name. // Get all label values that are associated with a given label name.
GetAllValuesForLabel(model.LabelName) (model.LabelValues, error) GetAllValuesForLabel(clientmodel.LabelName) (clientmodel.LabelValues, error)
// Requests the storage stack to build a materialized View of the values // Requests the storage stack to build a materialized View of the values
// contained therein. // contained therein.
@ -65,17 +67,17 @@ type MetricPersistence interface {
// View provides a view of the values in the datastore subject to the request // View provides a view of the values in the datastore subject to the request
// of a preloading operation. // of a preloading operation.
type View interface { type View interface {
GetValueAtTime(*model.Fingerprint, time.Time) model.Values GetValueAtTime(*clientmodel.Fingerprint, time.Time) Values
GetBoundaryValues(*model.Fingerprint, model.Interval) model.Values GetBoundaryValues(*clientmodel.Fingerprint, Interval) Values
GetRangeValues(*model.Fingerprint, model.Interval) model.Values GetRangeValues(*clientmodel.Fingerprint, Interval) Values
// Destroy this view. // Destroy this view.
Close() Close()
} }
type Series interface { type Series interface {
Fingerprint() *model.Fingerprint Fingerprint() *clientmodel.Fingerprint
Metric() model.Metric Metric() clientmodel.Metric
} }
type IteratorsForFingerprintBuilder interface { type IteratorsForFingerprintBuilder interface {

View file

@ -11,29 +11,47 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package model package metric
import (
clientmodel "github.com/prometheus/client_golang/model"
)
type LabelPair struct { type LabelPair struct {
Name LabelName Name clientmodel.LabelName
Value LabelValue Value clientmodel.LabelValue
} }
type LabelPairs []LabelPair func (l *LabelPair) Equal(o *LabelPair) bool {
switch {
case l.Name != o.Name:
return false
case l.Value != o.Value:
return false
default:
return true
}
}
type LabelPairs []*LabelPair
func (l LabelPairs) Len() int { func (l LabelPairs) Len() int {
return len(l) return len(l)
} }
func (l LabelPairs) Less(i, j int) bool { func (l LabelPairs) Less(i, j int) bool {
if l[i].Name < l[j].Name { switch {
case l[i].Name > l[j].Name:
return false
case l[i].Name < l[j].Name:
return true return true
} case l[i].Value > l[j].Value:
return false
if l[i].Value < l[j].Value { case l[i].Value < l[j].Value:
return true return true
default:
return false
} }
return false
} }
func (l LabelPairs) Swap(i, j int) { func (l LabelPairs) Swap(i, j int) {

View file

@ -11,12 +11,13 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package model package metric
import ( import (
"github.com/prometheus/prometheus/utility/test"
"sort" "sort"
"testing" "testing"
"github.com/prometheus/prometheus/utility/test"
) )
func testLabelPairs(t test.Tester) { func testLabelPairs(t test.Tester) {
@ -66,7 +67,7 @@ func testLabelPairs(t test.Tester) {
sort.Sort(scenario.in) sort.Sort(scenario.in)
for j, expected := range scenario.out { for j, expected := range scenario.out {
if expected != scenario.in[j] { if !expected.Equal(scenario.in[j]) {
t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j]) t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j])
} }
} }

View file

@ -23,10 +23,11 @@ import (
"code.google.com/p/goprotobuf/proto" "code.google.com/p/goprotobuf/proto"
clientmodel "github.com/prometheus/client_golang/model"
dto "github.com/prometheus/prometheus/model/generated" dto "github.com/prometheus/prometheus/model/generated"
index "github.com/prometheus/prometheus/storage/raw/index/leveldb" index "github.com/prometheus/prometheus/storage/raw/index/leveldb"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/storage/raw/leveldb" "github.com/prometheus/prometheus/storage/raw/leveldb"
"github.com/prometheus/prometheus/utility" "github.com/prometheus/prometheus/utility"
@ -92,7 +93,7 @@ func (l *LevelDBMetricPersistence) Close() {
func NewLevelDBMetricPersistence(baseDirectory string) (*LevelDBMetricPersistence, error) { func NewLevelDBMetricPersistence(baseDirectory string) (*LevelDBMetricPersistence, error) {
workers := utility.NewUncertaintyGroup(7) workers := utility.NewUncertaintyGroup(7)
emission := &LevelDBMetricPersistence{} emission := new(LevelDBMetricPersistence)
var subsystemOpeners = []struct { var subsystemOpeners = []struct {
name string name string
@ -172,14 +173,14 @@ func NewLevelDBMetricPersistence(baseDirectory string) (*LevelDBMetricPersistenc
return emission, nil return emission, nil
} }
func (l *LevelDBMetricPersistence) AppendSample(sample model.Sample) (err error) { func (l *LevelDBMetricPersistence) AppendSample(sample *clientmodel.Sample) (err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
duration := time.Since(begin) duration := time.Since(begin)
recordOutcome(duration, err, map[string]string{operation: appendSample, result: success}, map[string]string{operation: appendSample, result: failure}) recordOutcome(duration, err, map[string]string{operation: appendSample, result: success}, map[string]string{operation: appendSample, result: failure})
}(time.Now()) }(time.Now())
err = l.AppendSamples(model.Samples{sample}) err = l.AppendSamples(clientmodel.Samples{sample})
return return
} }
@ -187,30 +188,28 @@ func (l *LevelDBMetricPersistence) AppendSample(sample model.Sample) (err error)
// groupByFingerprint collects all of the provided samples, groups them // groupByFingerprint collects all of the provided samples, groups them
// together by their respective metric fingerprint, and finally sorts // together by their respective metric fingerprint, and finally sorts
// them chronologically. // them chronologically.
func groupByFingerprint(samples model.Samples) map[model.Fingerprint]model.Samples { func groupByFingerprint(samples clientmodel.Samples) map[clientmodel.Fingerprint]clientmodel.Samples {
fingerprintToSamples := map[model.Fingerprint]model.Samples{} fingerprintToSamples := map[clientmodel.Fingerprint]clientmodel.Samples{}
for _, sample := range samples { for _, sample := range samples {
fingerprint := *model.NewFingerprintFromMetric(sample.Metric) fingerprint := &clientmodel.Fingerprint{}
samples := fingerprintToSamples[fingerprint] fingerprint.LoadFromMetric(sample.Metric)
samples := fingerprintToSamples[*fingerprint]
samples = append(samples, sample) samples = append(samples, sample)
fingerprintToSamples[fingerprint] = samples fingerprintToSamples[*fingerprint] = samples
} }
sortingSemaphore := make(chan bool, sortConcurrency) sortingSemaphore := make(chan bool, sortConcurrency)
doneSorting := sync.WaitGroup{} doneSorting := sync.WaitGroup{}
for i := 0; i < sortConcurrency; i++ {
sortingSemaphore <- true
}
for _, samples := range fingerprintToSamples { for _, samples := range fingerprintToSamples {
doneSorting.Add(1) doneSorting.Add(1)
<-sortingSemaphore sortingSemaphore <- true
go func(samples model.Samples) { go func(samples clientmodel.Samples) {
sort.Sort(samples) sort.Sort(samples)
sortingSemaphore <- true
<-sortingSemaphore
doneSorting.Done() doneSorting.Done()
}(samples) }(samples)
} }
@ -223,18 +222,18 @@ func groupByFingerprint(samples model.Samples) map[model.Fingerprint]model.Sampl
// findUnindexedMetrics scours the metric membership index for each given Metric // findUnindexedMetrics scours the metric membership index for each given Metric
// in the keyspace and returns a map of Fingerprint-Metric pairs that are // in the keyspace and returns a map of Fingerprint-Metric pairs that are
// absent. // absent.
func (l *LevelDBMetricPersistence) findUnindexedMetrics(candidates map[model.Fingerprint]model.Metric) (unindexed map[model.Fingerprint]model.Metric, err error) { func (l *LevelDBMetricPersistence) findUnindexedMetrics(candidates map[clientmodel.Fingerprint]clientmodel.Metric) (unindexed map[clientmodel.Fingerprint]clientmodel.Metric, err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
duration := time.Since(begin) duration := time.Since(begin)
recordOutcome(duration, err, map[string]string{operation: findUnindexedMetrics, result: success}, map[string]string{operation: findUnindexedMetrics, result: failure}) recordOutcome(duration, err, map[string]string{operation: findUnindexedMetrics, result: success}, map[string]string{operation: findUnindexedMetrics, result: failure})
}(time.Now()) }(time.Now())
unindexed = make(map[model.Fingerprint]model.Metric) unindexed = make(map[clientmodel.Fingerprint]clientmodel.Metric)
// Determine which metrics are unknown in the database. dto := &dto.Metric{}
for fingerprint, metric := range candidates { for fingerprint, metric := range candidates {
dto := model.MetricToDTO(metric) dumpMetric(dto, metric)
indexHas, err := l.hasIndexMetric(dto) indexHas, err := l.hasIndexMetric(dto)
if err != nil { if err != nil {
return unindexed, err return unindexed, err
@ -244,7 +243,7 @@ func (l *LevelDBMetricPersistence) findUnindexedMetrics(candidates map[model.Fin
} }
} }
return return unindexed, nil
} }
// indexLabelNames accumulates all label name to fingerprint index entries for // indexLabelNames accumulates all label name to fingerprint index entries for
@ -252,14 +251,14 @@ func (l *LevelDBMetricPersistence) findUnindexedMetrics(candidates map[model.Fin
// the index to reflect the new state. // the index to reflect the new state.
// //
// This operation is idempotent. // This operation is idempotent.
func (l *LevelDBMetricPersistence) indexLabelNames(metrics map[model.Fingerprint]model.Metric) (err error) { func (l *LevelDBMetricPersistence) indexLabelNames(metrics map[clientmodel.Fingerprint]clientmodel.Metric) (err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
duration := time.Since(begin) duration := time.Since(begin)
recordOutcome(duration, err, map[string]string{operation: indexLabelNames, result: success}, map[string]string{operation: indexLabelNames, result: failure}) recordOutcome(duration, err, map[string]string{operation: indexLabelNames, result: success}, map[string]string{operation: indexLabelNames, result: failure})
}(time.Now()) }(time.Now())
labelNameFingerprints := map[model.LabelName]utility.Set{} labelNameFingerprints := map[clientmodel.LabelName]utility.Set{}
for fingerprint, metric := range metrics { for fingerprint, metric := range metrics {
for labelName := range metric { for labelName := range metric {
@ -286,9 +285,9 @@ func (l *LevelDBMetricPersistence) indexLabelNames(metrics map[model.Fingerprint
defer batch.Close() defer batch.Close()
for labelName, fingerprintSet := range labelNameFingerprints { for labelName, fingerprintSet := range labelNameFingerprints {
fingerprints := model.Fingerprints{} fingerprints := clientmodel.Fingerprints{}
for e := range fingerprintSet { for e := range fingerprintSet {
fingerprint := e.(model.Fingerprint) fingerprint := e.(clientmodel.Fingerprint)
fingerprints = append(fingerprints, &fingerprint) fingerprints = append(fingerprints, &fingerprint)
} }
@ -297,9 +296,11 @@ func (l *LevelDBMetricPersistence) indexLabelNames(metrics map[model.Fingerprint
key := &dto.LabelName{ key := &dto.LabelName{
Name: proto.String(string(labelName)), Name: proto.String(string(labelName)),
} }
value := &dto.FingerprintCollection{} value := new(dto.FingerprintCollection)
for _, fingerprint := range fingerprints { for _, fingerprint := range fingerprints {
value.Member = append(value.Member, fingerprint.ToDTO()) f := new(dto.Fingerprint)
dumpFingerprint(f, fingerprint)
value.Member = append(value.Member, f)
} }
batch.Put(key, value) batch.Put(key, value)
@ -318,18 +319,18 @@ func (l *LevelDBMetricPersistence) indexLabelNames(metrics map[model.Fingerprint
// the index to reflect the new state. // the index to reflect the new state.
// //
// This operation is idempotent. // This operation is idempotent.
func (l *LevelDBMetricPersistence) indexLabelPairs(metrics map[model.Fingerprint]model.Metric) (err error) { func (l *LevelDBMetricPersistence) indexLabelPairs(metrics map[clientmodel.Fingerprint]clientmodel.Metric) (err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
duration := time.Since(begin) duration := time.Since(begin)
recordOutcome(duration, err, map[string]string{operation: indexLabelPairs, result: success}, map[string]string{operation: indexLabelPairs, result: failure}) recordOutcome(duration, err, map[string]string{operation: indexLabelPairs, result: success}, map[string]string{operation: indexLabelPairs, result: failure})
}(time.Now()) }(time.Now())
labelPairFingerprints := map[model.LabelPair]utility.Set{} labelPairFingerprints := map[LabelPair]utility.Set{}
for fingerprint, metric := range metrics { for fingerprint, metric := range metrics {
for labelName, labelValue := range metric { for labelName, labelValue := range metric {
labelPair := model.LabelPair{ labelPair := LabelPair{
Name: labelName, Name: labelName,
Value: labelValue, Value: labelValue,
} }
@ -337,7 +338,7 @@ func (l *LevelDBMetricPersistence) indexLabelPairs(metrics map[model.Fingerprint
if !ok { if !ok {
fingerprintSet = utility.Set{} fingerprintSet = utility.Set{}
fingerprints, err := l.GetFingerprintsForLabelSet(model.LabelSet{ fingerprints, err := l.GetFingerprintsForLabelSet(clientmodel.LabelSet{
labelName: labelValue, labelName: labelValue,
}) })
if err != nil { if err != nil {
@ -358,9 +359,9 @@ func (l *LevelDBMetricPersistence) indexLabelPairs(metrics map[model.Fingerprint
defer batch.Close() defer batch.Close()
for labelPair, fingerprintSet := range labelPairFingerprints { for labelPair, fingerprintSet := range labelPairFingerprints {
fingerprints := model.Fingerprints{} fingerprints := clientmodel.Fingerprints{}
for e := range fingerprintSet { for e := range fingerprintSet {
fingerprint := e.(model.Fingerprint) fingerprint := e.(clientmodel.Fingerprint)
fingerprints = append(fingerprints, &fingerprint) fingerprints = append(fingerprints, &fingerprint)
} }
@ -370,9 +371,11 @@ func (l *LevelDBMetricPersistence) indexLabelPairs(metrics map[model.Fingerprint
Name: proto.String(string(labelPair.Name)), Name: proto.String(string(labelPair.Name)),
Value: proto.String(string(labelPair.Value)), Value: proto.String(string(labelPair.Value)),
} }
value := &dto.FingerprintCollection{} value := new(dto.FingerprintCollection)
for _, fingerprint := range fingerprints { for _, fingerprint := range fingerprints {
value.Member = append(value.Member, fingerprint.ToDTO()) f := new(dto.Fingerprint)
dumpFingerprint(f, fingerprint)
value.Member = append(value.Member, f)
} }
batch.Put(key, value) batch.Put(key, value)
@ -390,7 +393,7 @@ func (l *LevelDBMetricPersistence) indexLabelPairs(metrics map[model.Fingerprint
// in the index and then bulk updates. // in the index and then bulk updates.
// //
// This operation is idempotent. // This operation is idempotent.
func (l *LevelDBMetricPersistence) indexFingerprints(metrics map[model.Fingerprint]model.Metric) (err error) { func (l *LevelDBMetricPersistence) indexFingerprints(metrics map[clientmodel.Fingerprint]clientmodel.Metric) (err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
duration := time.Since(begin) duration := time.Since(begin)
@ -401,7 +404,11 @@ func (l *LevelDBMetricPersistence) indexFingerprints(metrics map[model.Fingerpri
defer batch.Close() defer batch.Close()
for fingerprint, metric := range metrics { for fingerprint, metric := range metrics {
batch.Put(fingerprint.ToDTO(), model.MetricToDTO(metric)) f := new(dto.Fingerprint)
dumpFingerprint(f, &fingerprint)
m := &dto.Metric{}
dumpMetric(m, metric)
batch.Put(f, m)
} }
err = l.fingerprintToMetrics.Commit(batch) err = l.fingerprintToMetrics.Commit(batch)
@ -417,7 +424,7 @@ var existenceIdentity = &dto.MembershipIndexValue{}
// indexMetrics takes groups of samples, determines which ones contain metrics // indexMetrics takes groups of samples, determines which ones contain metrics
// that are unknown to the storage stack, and then proceeds to update all // that are unknown to the storage stack, and then proceeds to update all
// affected indices. // affected indices.
func (l *LevelDBMetricPersistence) indexMetrics(fingerprints map[model.Fingerprint]model.Metric) (err error) { func (l *LevelDBMetricPersistence) indexMetrics(fingerprints map[clientmodel.Fingerprint]clientmodel.Metric) (err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
duration := time.Since(begin) duration := time.Since(begin)
@ -425,7 +432,7 @@ func (l *LevelDBMetricPersistence) indexMetrics(fingerprints map[model.Fingerpri
}(time.Now()) }(time.Now())
var ( var (
absentMetrics map[model.Fingerprint]model.Metric absentMetrics map[clientmodel.Fingerprint]clientmodel.Metric
) )
absentMetrics, err = l.findUnindexedMetrics(fingerprints) absentMetrics, err = l.findUnindexedMetrics(fingerprints)
@ -466,7 +473,9 @@ func (l *LevelDBMetricPersistence) indexMetrics(fingerprints map[model.Fingerpri
defer batch.Close() defer batch.Close()
for _, metric := range absentMetrics { for _, metric := range absentMetrics {
batch.Put(model.MetricToDTO(metric), existenceIdentity) m := &dto.Metric{}
dumpMetric(m, metric)
batch.Put(m, existenceIdentity)
} }
err = l.metricMembershipIndex.Commit(batch) err = l.metricMembershipIndex.Commit(batch)
@ -478,7 +487,7 @@ func (l *LevelDBMetricPersistence) indexMetrics(fingerprints map[model.Fingerpri
return return
} }
func (l *LevelDBMetricPersistence) refreshHighWatermarks(groups map[model.Fingerprint]model.Samples) (err error) { func (l *LevelDBMetricPersistence) refreshHighWatermarks(groups map[clientmodel.Fingerprint]clientmodel.Samples) (err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
duration := time.Since(begin) duration := time.Since(begin)
@ -491,7 +500,9 @@ func (l *LevelDBMetricPersistence) refreshHighWatermarks(groups map[model.Finger
value := &dto.MetricHighWatermark{} value := &dto.MetricHighWatermark{}
for fingerprint, samples := range groups { for fingerprint, samples := range groups {
value.Reset() value.Reset()
present, err := l.MetricHighWatermarks.Get(fingerprint.ToDTO(), value) f := new(dto.Fingerprint)
dumpFingerprint(f, &fingerprint)
present, err := l.MetricHighWatermarks.Get(f, value)
if err != nil { if err != nil {
return err return err
} }
@ -500,7 +511,7 @@ func (l *LevelDBMetricPersistence) refreshHighWatermarks(groups map[model.Finger
if !present { if !present {
value.Timestamp = proto.Int64(newestSampleTimestamp.Unix()) value.Timestamp = proto.Int64(newestSampleTimestamp.Unix())
batch.Put(fingerprint.ToDTO(), value) batch.Put(f, value)
continue continue
} }
@ -508,7 +519,7 @@ func (l *LevelDBMetricPersistence) refreshHighWatermarks(groups map[model.Finger
// BUG(matt): Repace this with watermark management. // BUG(matt): Repace this with watermark management.
if newestSampleTimestamp.After(time.Unix(value.GetTimestamp(), 0)) { if newestSampleTimestamp.After(time.Unix(value.GetTimestamp(), 0)) {
value.Timestamp = proto.Int64(newestSampleTimestamp.Unix()) value.Timestamp = proto.Int64(newestSampleTimestamp.Unix())
batch.Put(fingerprint.ToDTO(), value) batch.Put(f, value)
} }
} }
@ -520,7 +531,7 @@ func (l *LevelDBMetricPersistence) refreshHighWatermarks(groups map[model.Finger
return nil return nil
} }
func (l *LevelDBMetricPersistence) AppendSamples(samples model.Samples) (err error) { func (l *LevelDBMetricPersistence) AppendSamples(samples clientmodel.Samples) (err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
duration := time.Since(begin) duration := time.Since(begin)
@ -531,8 +542,8 @@ func (l *LevelDBMetricPersistence) AppendSamples(samples model.Samples) (err err
indexErrChan := make(chan error, 1) indexErrChan := make(chan error, 1)
watermarkErrChan := make(chan error, 1) watermarkErrChan := make(chan error, 1)
go func(groups map[model.Fingerprint]model.Samples) { go func(groups map[clientmodel.Fingerprint]clientmodel.Samples) {
metrics := map[model.Fingerprint]model.Metric{} metrics := map[clientmodel.Fingerprint]clientmodel.Metric{}
for fingerprint, samples := range groups { for fingerprint, samples := range groups {
metrics[fingerprint] = samples[0].Metric metrics[fingerprint] = samples[0].Metric
@ -541,7 +552,7 @@ func (l *LevelDBMetricPersistence) AppendSamples(samples model.Samples) (err err
indexErrChan <- l.indexMetrics(metrics) indexErrChan <- l.indexMetrics(metrics)
}(fingerprintToSamples) }(fingerprintToSamples)
go func(groups map[model.Fingerprint]model.Samples) { go func(groups map[clientmodel.Fingerprint]clientmodel.Samples) {
watermarkErrChan <- l.refreshHighWatermarks(groups) watermarkErrChan <- l.refreshHighWatermarks(groups)
}(fingerprintToSamples) }(fingerprintToSamples)
@ -564,22 +575,24 @@ func (l *LevelDBMetricPersistence) AppendSamples(samples model.Samples) (err err
chunk := group[0:take] chunk := group[0:take]
group = group[take:lengthOfGroup] group = group[take:lengthOfGroup]
key := model.SampleKey{ key := SampleKey{
Fingerprint: &fingerprint, Fingerprint: &fingerprint,
FirstTimestamp: chunk[0].Timestamp, FirstTimestamp: chunk[0].Timestamp,
LastTimestamp: chunk[take-1].Timestamp, LastTimestamp: chunk[take-1].Timestamp,
SampleCount: uint32(take), SampleCount: uint32(take),
}.ToDTO() }
value := &dto.SampleValueSeries{} value := &dto.SampleValueSeries{}
for _, sample := range chunk { for _, sample := range chunk {
value.Value = append(value.Value, &dto.SampleValueSeries_Value{ value.Value = append(value.Value, &dto.SampleValueSeries_Value{
Timestamp: proto.Int64(sample.Timestamp.Unix()), Timestamp: proto.Int64(sample.Timestamp.Unix()),
Value: sample.Value.ToDTO(), Value: proto.Float64(float64(sample.Value)),
}) })
} }
samplesBatch.Put(key, value) k := &dto.SampleKey{}
key.Dump(k)
samplesBatch.Put(k, value)
} }
} }
@ -601,48 +614,27 @@ func (l *LevelDBMetricPersistence) AppendSamples(samples model.Samples) (err err
return return
} }
func extractSampleKey(i leveldb.Iterator) (key model.SampleKey, err error) { func extractSampleKey(i leveldb.Iterator) (*SampleKey, error) {
k := &dto.SampleKey{} k := &dto.SampleKey{}
err = proto.Unmarshal(i.Key(), k) err := proto.Unmarshal(i.Key(), k)
if err != nil { if err != nil {
return return nil, err
} }
key = model.NewSampleKeyFromDTO(k) key := &SampleKey{}
key.Load(k)
return return key, nil
} }
func extractSampleValues(i leveldb.Iterator) (values model.Values, err error) { func extractSampleValues(i leveldb.Iterator) (Values, error) {
v := &dto.SampleValueSeries{} v := &dto.SampleValueSeries{}
err = proto.Unmarshal(i.Value(), v) err := proto.Unmarshal(i.Value(), v)
if err != nil { if err != nil {
return return nil, err
} }
values = model.NewValuesFromDTO(v) return NewValuesFromDTO(v), nil
return
}
func fingerprintsEqual(l *dto.Fingerprint, r *dto.Fingerprint) bool {
if l == r {
return true
}
if l == nil && r == nil {
return true
}
if r.Signature == l.Signature {
return true
}
if *r.Signature == *l.Signature {
return true
}
return false
} }
func (l *LevelDBMetricPersistence) hasIndexMetric(dto *dto.Metric) (value bool, err error) { func (l *LevelDBMetricPersistence) hasIndexMetric(dto *dto.Metric) (value bool, err error) {
@ -681,7 +673,7 @@ func (l *LevelDBMetricPersistence) HasLabelName(dto *dto.LabelName) (value bool,
return return
} }
func (l *LevelDBMetricPersistence) GetFingerprintsForLabelSet(labelSet model.LabelSet) (fps model.Fingerprints, err error) { func (l *LevelDBMetricPersistence) GetFingerprintsForLabelSet(labelSet clientmodel.LabelSet) (fps clientmodel.Fingerprints, err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
duration := time.Since(begin) duration := time.Since(begin)
@ -689,10 +681,17 @@ func (l *LevelDBMetricPersistence) GetFingerprintsForLabelSet(labelSet model.Lab
}(time.Now()) }(time.Now())
sets := []utility.Set{} sets := []utility.Set{}
pair := &dto.LabelPair{}
unmarshaled := new(dto.FingerprintCollection)
for _, labelSetDTO := range model.LabelSetToDTOs(&labelSet) { for name, value := range labelSet {
unmarshaled := &dto.FingerprintCollection{} pair.Reset()
present, err := l.labelSetToFingerprints.Get(labelSetDTO, unmarshaled) unmarshaled.Reset()
pair.Name = proto.String(string(name))
pair.Value = proto.String(string(value))
present, err := l.labelSetToFingerprints.Get(pair, unmarshaled)
if err != nil { if err != nil {
return fps, err return fps, err
} }
@ -703,7 +702,8 @@ func (l *LevelDBMetricPersistence) GetFingerprintsForLabelSet(labelSet model.Lab
set := utility.Set{} set := utility.Set{}
for _, m := range unmarshaled.Member { for _, m := range unmarshaled.Member {
fp := model.NewFingerprintFromRowKey(*m.Signature) fp := &clientmodel.Fingerprint{}
loadFingerprint(fp, m)
set.Add(*fp) set.Add(*fp)
} }
@ -712,7 +712,7 @@ func (l *LevelDBMetricPersistence) GetFingerprintsForLabelSet(labelSet model.Lab
numberOfSets := len(sets) numberOfSets := len(sets)
if numberOfSets == 0 { if numberOfSets == 0 {
return return nil, nil
} }
base := sets[0] base := sets[0]
@ -720,22 +720,24 @@ func (l *LevelDBMetricPersistence) GetFingerprintsForLabelSet(labelSet model.Lab
base = base.Intersection(sets[i]) base = base.Intersection(sets[i])
} }
for _, e := range base.Elements() { for _, e := range base.Elements() {
fingerprint := e.(model.Fingerprint) fingerprint := e.(clientmodel.Fingerprint)
fps = append(fps, &fingerprint) fps = append(fps, &fingerprint)
} }
return return fps, nil
} }
func (l *LevelDBMetricPersistence) GetFingerprintsForLabelName(labelName model.LabelName) (fps model.Fingerprints, err error) { func (l *LevelDBMetricPersistence) GetFingerprintsForLabelName(labelName clientmodel.LabelName) (fps clientmodel.Fingerprints, err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
duration := time.Since(begin) duration := time.Since(begin)
recordOutcome(duration, err, map[string]string{operation: getFingerprintsForLabelName, result: success}, map[string]string{operation: getFingerprintsForLabelName, result: failure}) recordOutcome(duration, err, map[string]string{operation: getFingerprintsForLabelName, result: success}, map[string]string{operation: getFingerprintsForLabelName, result: failure})
}(time.Now()) }(time.Now())
unmarshaled := &dto.FingerprintCollection{} unmarshaled := new(dto.FingerprintCollection)
present, err := l.labelNameToFingerprints.Get(model.LabelNameToDTO(&labelName), unmarshaled) d := &dto.LabelName{}
dumpLabelName(d, labelName)
present, err := l.labelNameToFingerprints.Get(d, unmarshaled)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -744,14 +746,15 @@ func (l *LevelDBMetricPersistence) GetFingerprintsForLabelName(labelName model.L
} }
for _, m := range unmarshaled.Member { for _, m := range unmarshaled.Member {
fp := model.NewFingerprintFromRowKey(*m.Signature) fp := &clientmodel.Fingerprint{}
loadFingerprint(fp, m)
fps = append(fps, fp) fps = append(fps, fp)
} }
return fps, nil return fps, nil
} }
func (l *LevelDBMetricPersistence) GetMetricForFingerprint(f *model.Fingerprint) (m model.Metric, err error) { func (l *LevelDBMetricPersistence) GetMetricForFingerprint(f *clientmodel.Fingerprint) (m clientmodel.Metric, err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
duration := time.Since(begin) duration := time.Since(begin)
@ -759,7 +762,9 @@ func (l *LevelDBMetricPersistence) GetMetricForFingerprint(f *model.Fingerprint)
}(time.Now()) }(time.Now())
unmarshaled := &dto.Metric{} unmarshaled := &dto.Metric{}
present, err := l.fingerprintToMetrics.Get(model.FingerprintToDTO(f), unmarshaled) d := new(dto.Fingerprint)
dumpFingerprint(d, f)
present, err := l.fingerprintToMetrics.Get(d, unmarshaled)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -767,24 +772,24 @@ func (l *LevelDBMetricPersistence) GetMetricForFingerprint(f *model.Fingerprint)
return nil, nil return nil, nil
} }
m = model.Metric{} m = clientmodel.Metric{}
for _, v := range unmarshaled.LabelPair { for _, v := range unmarshaled.LabelPair {
m[model.LabelName(*v.Name)] = model.LabelValue(*v.Value) m[clientmodel.LabelName(v.GetName())] = clientmodel.LabelValue(v.GetValue())
} }
return m, nil return m, nil
} }
func (l LevelDBMetricPersistence) GetValueAtTime(f *model.Fingerprint, t time.Time) model.Values { func (l *LevelDBMetricPersistence) GetValueAtTime(f *clientmodel.Fingerprint, t time.Time) Values {
panic("Not implemented") panic("Not implemented")
} }
func (l LevelDBMetricPersistence) GetBoundaryValues(f *model.Fingerprint, i model.Interval) model.Values { func (l *LevelDBMetricPersistence) GetBoundaryValues(f *clientmodel.Fingerprint, i Interval) Values {
panic("Not implemented") panic("Not implemented")
} }
func (l *LevelDBMetricPersistence) GetRangeValues(f *model.Fingerprint, i model.Interval) model.Values { func (l *LevelDBMetricPersistence) GetRangeValues(f *clientmodel.Fingerprint, i Interval) Values {
panic("Not implemented") panic("Not implemented")
} }
@ -797,9 +802,9 @@ func (d *MetricKeyDecoder) DecodeKey(in interface{}) (out interface{}, err error
return return
} }
out = model.LabelPair{ out = LabelPair{
Name: model.LabelName(*unmarshaled.Name), Name: clientmodel.LabelName(*unmarshaled.Name),
Value: model.LabelValue(*unmarshaled.Value), Value: clientmodel.LabelValue(*unmarshaled.Value),
} }
return return
@ -810,11 +815,11 @@ func (d *MetricKeyDecoder) DecodeValue(in interface{}) (out interface{}, err err
} }
type LabelNameFilter struct { type LabelNameFilter struct {
labelName model.LabelName labelName clientmodel.LabelName
} }
func (f LabelNameFilter) Filter(key, value interface{}) (filterResult storage.FilterResult) { func (f LabelNameFilter) Filter(key, value interface{}) (filterResult storage.FilterResult) {
labelPair, ok := key.(model.LabelPair) labelPair, ok := key.(LabelPair)
if ok && labelPair.Name == f.labelName { if ok && labelPair.Name == f.labelName {
return storage.ACCEPT return storage.ACCEPT
} }
@ -822,16 +827,16 @@ func (f LabelNameFilter) Filter(key, value interface{}) (filterResult storage.Fi
} }
type CollectLabelValuesOp struct { type CollectLabelValuesOp struct {
labelValues []model.LabelValue labelValues []clientmodel.LabelValue
} }
func (op *CollectLabelValuesOp) Operate(key, value interface{}) (err *storage.OperatorError) { func (op *CollectLabelValuesOp) Operate(key, value interface{}) (err *storage.OperatorError) {
labelPair := key.(model.LabelPair) labelPair := key.(LabelPair)
op.labelValues = append(op.labelValues, model.LabelValue(labelPair.Value)) op.labelValues = append(op.labelValues, clientmodel.LabelValue(labelPair.Value))
return return
} }
func (l *LevelDBMetricPersistence) GetAllValuesForLabel(labelName model.LabelName) (values model.LabelValues, err error) { func (l *LevelDBMetricPersistence) GetAllValuesForLabel(labelName clientmodel.LabelName) (values clientmodel.LabelValues, err error) {
filter := &LabelNameFilter{ filter := &LabelNameFilter{
labelName: labelName, labelName: labelName,
} }

View file

@ -18,7 +18,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/prometheus/prometheus/model" clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/utility" "github.com/prometheus/prometheus/utility"
) )
@ -29,49 +30,49 @@ const initialSeriesArenaSize = 4 * 60
// Models a given sample entry stored in the in-memory arena. // Models a given sample entry stored in the in-memory arena.
type value interface { type value interface {
// Gets the given value. // Gets the given value.
get() model.SampleValue get() clientmodel.SampleValue
} }
// Models a single sample value. It presumes that there is either no subsequent // Models a single sample value. It presumes that there is either no subsequent
// value seen or that any subsequent values are of a different value. // value seen or that any subsequent values are of a different value.
type singletonValue model.SampleValue type singletonValue clientmodel.SampleValue
func (v singletonValue) get() model.SampleValue { func (v singletonValue) get() clientmodel.SampleValue {
return model.SampleValue(v) return clientmodel.SampleValue(v)
} }
type stream struct { type stream struct {
sync.RWMutex sync.RWMutex
metric model.Metric metric clientmodel.Metric
values model.Values values Values
} }
func (s *stream) add(timestamp time.Time, value model.SampleValue) { func (s *stream) add(timestamp time.Time, value clientmodel.SampleValue) {
s.Lock() s.Lock()
defer s.Unlock() defer s.Unlock()
// BUG(all): https://github.com/prometheus/prometheus/pull/265/files#r4336435. // BUG(all): https://github.com/prometheus/prometheus/pull/265/files#r4336435.
s.values = append(s.values, model.SamplePair{ s.values = append(s.values, &SamplePair{
Timestamp: timestamp, Timestamp: timestamp,
Value: value, Value: value,
}) })
} }
func (s *stream) clone() model.Values { func (s *stream) clone() Values {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
// BUG(all): Examine COW technique. // BUG(all): Examine COW technique.
clone := make(model.Values, len(s.values)) clone := make(Values, len(s.values))
copy(clone, s.values) copy(clone, s.values)
return clone return clone
} }
func (s *stream) getValueAtTime(t time.Time) model.Values { func (s *stream) getValueAtTime(t time.Time) Values {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -79,29 +80,29 @@ func (s *stream) getValueAtTime(t time.Time) model.Values {
l := len(s.values) l := len(s.values)
switch l { switch l {
case 0: case 0:
return model.Values{} return Values{}
case 1: case 1:
return model.Values{s.values[0]} return Values{s.values[0]}
default: default:
index := sort.Search(l, func(i int) bool { index := sort.Search(l, func(i int) bool {
return !s.values[i].Timestamp.Before(t) return !s.values[i].Timestamp.Before(t)
}) })
if index == 0 { if index == 0 {
return model.Values{s.values[0]} return Values{s.values[0]}
} }
if index == l { if index == l {
return model.Values{s.values[l-1]} return Values{s.values[l-1]}
} }
if s.values[index].Timestamp.Equal(t) { if s.values[index].Timestamp.Equal(t) {
return model.Values{s.values[index]} return Values{s.values[index]}
} }
return model.Values{s.values[index-1], s.values[index]} return Values{s.values[index-1], s.values[index]}
} }
} }
func (s *stream) getBoundaryValues(in model.Interval) model.Values { func (s *stream) getBoundaryValues(in Interval) Values {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -116,15 +117,15 @@ func (s *stream) getBoundaryValues(in model.Interval) model.Values {
resultRange := s.values[oldest:newest] resultRange := s.values[oldest:newest]
switch len(resultRange) { switch len(resultRange) {
case 0: case 0:
return model.Values{} return Values{}
case 1: case 1:
return model.Values{resultRange[0]} return Values{resultRange[0]}
default: default:
return model.Values{resultRange[0], resultRange[len(resultRange)-1]} return Values{resultRange[0], resultRange[len(resultRange)-1]}
} }
} }
func (s *stream) getRangeValues(in model.Interval) model.Values { func (s *stream) getRangeValues(in Interval) Values {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -136,7 +137,7 @@ func (s *stream) getRangeValues(in model.Interval) model.Values {
return s.values[i].Timestamp.After(in.NewestInclusive) return s.values[i].Timestamp.After(in.NewestInclusive)
}) })
result := make(model.Values, newest-oldest) result := make(Values, newest-oldest)
copy(result, s.values[oldest:newest]) copy(result, s.values[oldest:newest])
return result return result
@ -146,10 +147,10 @@ func (s *stream) empty() bool {
return len(s.values) == 0 return len(s.values) == 0
} }
func newStream(metric model.Metric) *stream { func newStream(metric clientmodel.Metric) *stream {
return &stream{ return &stream{
metric: metric, metric: metric,
values: make(model.Values, 0, initialSeriesArenaSize), values: make(Values, 0, initialSeriesArenaSize),
} }
} }
@ -157,9 +158,9 @@ type memorySeriesStorage struct {
sync.RWMutex sync.RWMutex
wmCache *WatermarkCache wmCache *WatermarkCache
fingerprintToSeries map[model.Fingerprint]*stream fingerprintToSeries map[clientmodel.Fingerprint]*stream
labelPairToFingerprints map[model.LabelPair]model.Fingerprints labelPairToFingerprints map[LabelPair]clientmodel.Fingerprints
labelNameToFingerprints map[model.LabelName]model.Fingerprints labelNameToFingerprints map[clientmodel.LabelName]clientmodel.Fingerprints
} }
type MemorySeriesOptions struct { type MemorySeriesOptions struct {
@ -168,7 +169,7 @@ type MemorySeriesOptions struct {
WatermarkCache *WatermarkCache WatermarkCache *WatermarkCache
} }
func (s *memorySeriesStorage) AppendSamples(samples model.Samples) error { func (s *memorySeriesStorage) AppendSamples(samples clientmodel.Samples) error {
for _, sample := range samples { for _, sample := range samples {
s.AppendSample(sample) s.AppendSample(sample)
} }
@ -176,30 +177,32 @@ func (s *memorySeriesStorage) AppendSamples(samples model.Samples) error {
return nil return nil
} }
func (s *memorySeriesStorage) AppendSample(sample model.Sample) error { func (s *memorySeriesStorage) AppendSample(sample *clientmodel.Sample) error {
s.Lock() s.Lock()
defer s.Unlock() defer s.Unlock()
fingerprint := model.NewFingerprintFromMetric(sample.Metric) fingerprint := &clientmodel.Fingerprint{}
fingerprint.LoadFromMetric(sample.Metric)
series := s.getOrCreateSeries(sample.Metric, fingerprint) series := s.getOrCreateSeries(sample.Metric, fingerprint)
series.add(sample.Timestamp, sample.Value) series.add(sample.Timestamp, sample.Value)
if s.wmCache != nil { if s.wmCache != nil {
s.wmCache.Set(fingerprint, &Watermarks{High: sample.Timestamp}) s.wmCache.Set(fingerprint, &watermarks{High: sample.Timestamp})
} }
return nil return nil
} }
func (s *memorySeriesStorage) CreateEmptySeries(metric model.Metric) { func (s *memorySeriesStorage) CreateEmptySeries(metric clientmodel.Metric) {
s.Lock() s.Lock()
defer s.Unlock() defer s.Unlock()
fingerprint := model.NewFingerprintFromMetric(metric) fingerprint := &clientmodel.Fingerprint{}
fingerprint.LoadFromMetric(metric)
s.getOrCreateSeries(metric, fingerprint) s.getOrCreateSeries(metric, fingerprint)
} }
func (s *memorySeriesStorage) getOrCreateSeries(metric model.Metric, fingerprint *model.Fingerprint) *stream { func (s *memorySeriesStorage) getOrCreateSeries(metric clientmodel.Metric, fingerprint *clientmodel.Fingerprint) *stream {
series, ok := s.fingerprintToSeries[*fingerprint] series, ok := s.fingerprintToSeries[*fingerprint]
if !ok { if !ok {
@ -207,7 +210,7 @@ func (s *memorySeriesStorage) getOrCreateSeries(metric model.Metric, fingerprint
s.fingerprintToSeries[*fingerprint] = series s.fingerprintToSeries[*fingerprint] = series
for k, v := range metric { for k, v := range metric {
labelPair := model.LabelPair{ labelPair := LabelPair{
Name: k, Name: k,
Value: v, Value: v,
} }
@ -223,8 +226,8 @@ func (s *memorySeriesStorage) getOrCreateSeries(metric model.Metric, fingerprint
return series return series
} }
func (s *memorySeriesStorage) Flush(flushOlderThan time.Time, queue chan<- model.Samples) { func (s *memorySeriesStorage) Flush(flushOlderThan time.Time, queue chan<- clientmodel.Samples) {
emptySeries := []model.Fingerprint{} emptySeries := clientmodel.Fingerprints{}
s.RLock() s.RLock()
for fingerprint, stream := range s.fingerprintToSeries { for fingerprint, stream := range s.fingerprintToSeries {
@ -237,10 +240,10 @@ func (s *memorySeriesStorage) Flush(flushOlderThan time.Time, queue chan<- model
i := sort.Search(len(stream.values), finder) i := sort.Search(len(stream.values), finder)
toArchive := stream.values[:i] toArchive := stream.values[:i]
toKeep := stream.values[i:] toKeep := stream.values[i:]
queued := make(model.Samples, 0, len(toArchive)) queued := make(clientmodel.Samples, 0, len(toArchive))
for _, value := range toArchive { for _, value := range toArchive {
queued = append(queued, model.Sample{ queued = append(queued, &clientmodel.Sample{
Metric: stream.metric, Metric: stream.metric,
Timestamp: value.Timestamp, Timestamp: value.Timestamp,
Value: value.Value, Value: value.Value,
@ -255,7 +258,7 @@ func (s *memorySeriesStorage) Flush(flushOlderThan time.Time, queue chan<- model
stream.values = toKeep stream.values = toKeep
if len(toKeep) == 0 { if len(toKeep) == 0 {
emptySeries = append(emptySeries, fingerprint) emptySeries = append(emptySeries, &fingerprint)
} }
stream.Unlock() stream.Unlock()
} }
@ -263,21 +266,21 @@ func (s *memorySeriesStorage) Flush(flushOlderThan time.Time, queue chan<- model
s.Lock() s.Lock()
for _, fingerprint := range emptySeries { for _, fingerprint := range emptySeries {
if s.fingerprintToSeries[fingerprint].empty() { if s.fingerprintToSeries[*fingerprint].empty() {
s.dropSeries(&fingerprint) s.dropSeries(fingerprint)
} }
} }
s.Unlock() s.Unlock()
} }
// Drop all references to a series, including any samples. // Drop all references to a series, including any samples.
func (s *memorySeriesStorage) dropSeries(fingerprint *model.Fingerprint) { func (s *memorySeriesStorage) dropSeries(fingerprint *clientmodel.Fingerprint) {
series, ok := s.fingerprintToSeries[*fingerprint] series, ok := s.fingerprintToSeries[*fingerprint]
if !ok { if !ok {
return return
} }
for k, v := range series.metric { for k, v := range series.metric {
labelPair := model.LabelPair{ labelPair := LabelPair{
Name: k, Name: k,
Value: v, Value: v,
} }
@ -289,14 +292,14 @@ func (s *memorySeriesStorage) dropSeries(fingerprint *model.Fingerprint) {
// Append raw samples, bypassing indexing. Only used to add data to views, // Append raw samples, bypassing indexing. Only used to add data to views,
// which don't need to lookup by metric. // which don't need to lookup by metric.
func (s *memorySeriesStorage) appendSamplesWithoutIndexing(fingerprint *model.Fingerprint, samples model.Values) { func (s *memorySeriesStorage) appendSamplesWithoutIndexing(fingerprint *clientmodel.Fingerprint, samples Values) {
s.Lock() s.Lock()
defer s.Unlock() defer s.Unlock()
series, ok := s.fingerprintToSeries[*fingerprint] series, ok := s.fingerprintToSeries[*fingerprint]
if !ok { if !ok {
series = newStream(model.Metric{}) series = newStream(clientmodel.Metric{})
s.fingerprintToSeries[*fingerprint] = series s.fingerprintToSeries[*fingerprint] = series
} }
@ -305,13 +308,13 @@ func (s *memorySeriesStorage) appendSamplesWithoutIndexing(fingerprint *model.Fi
} }
} }
func (s *memorySeriesStorage) GetFingerprintsForLabelSet(l model.LabelSet) (fingerprints model.Fingerprints, err error) { func (s *memorySeriesStorage) GetFingerprintsForLabelSet(l clientmodel.LabelSet) (fingerprints clientmodel.Fingerprints, err error) {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
sets := []utility.Set{} sets := []utility.Set{}
for k, v := range l { for k, v := range l {
values := s.labelPairToFingerprints[model.LabelPair{ values := s.labelPairToFingerprints[LabelPair{
Name: k, Name: k,
Value: v, Value: v,
}] }]
@ -332,14 +335,14 @@ func (s *memorySeriesStorage) GetFingerprintsForLabelSet(l model.LabelSet) (fing
base = base.Intersection(sets[i]) base = base.Intersection(sets[i])
} }
for _, e := range base.Elements() { for _, e := range base.Elements() {
fingerprint := e.(model.Fingerprint) fingerprint := e.(clientmodel.Fingerprint)
fingerprints = append(fingerprints, &fingerprint) fingerprints = append(fingerprints, &fingerprint)
} }
return fingerprints, nil return fingerprints, nil
} }
func (s *memorySeriesStorage) GetFingerprintsForLabelName(l model.LabelName) (model.Fingerprints, error) { func (s *memorySeriesStorage) GetFingerprintsForLabelName(l clientmodel.LabelName) (clientmodel.Fingerprints, error) {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -348,13 +351,13 @@ func (s *memorySeriesStorage) GetFingerprintsForLabelName(l model.LabelName) (mo
return nil, nil return nil, nil
} }
fingerprints := make(model.Fingerprints, len(values)) fingerprints := make(clientmodel.Fingerprints, len(values))
copy(fingerprints, values) copy(fingerprints, values)
return fingerprints, nil return fingerprints, nil
} }
func (s *memorySeriesStorage) GetMetricForFingerprint(f *model.Fingerprint) (model.Metric, error) { func (s *memorySeriesStorage) GetMetricForFingerprint(f *clientmodel.Fingerprint) (clientmodel.Metric, error) {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -363,7 +366,7 @@ func (s *memorySeriesStorage) GetMetricForFingerprint(f *model.Fingerprint) (mod
return nil, nil return nil, nil
} }
metric := model.Metric{} metric := clientmodel.Metric{}
for label, value := range series.metric { for label, value := range series.metric {
metric[label] = value metric[label] = value
} }
@ -371,7 +374,7 @@ func (s *memorySeriesStorage) GetMetricForFingerprint(f *model.Fingerprint) (mod
return metric, nil return metric, nil
} }
func (s *memorySeriesStorage) HasFingerprint(f *model.Fingerprint) bool { func (s *memorySeriesStorage) HasFingerprint(f *clientmodel.Fingerprint) bool {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -380,7 +383,7 @@ func (s *memorySeriesStorage) HasFingerprint(f *model.Fingerprint) bool {
return has return has
} }
func (s *memorySeriesStorage) CloneSamples(f *model.Fingerprint) model.Values { func (s *memorySeriesStorage) CloneSamples(f *clientmodel.Fingerprint) Values {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -392,7 +395,7 @@ func (s *memorySeriesStorage) CloneSamples(f *model.Fingerprint) model.Values {
return series.clone() return series.clone()
} }
func (s *memorySeriesStorage) GetValueAtTime(f *model.Fingerprint, t time.Time) model.Values { func (s *memorySeriesStorage) GetValueAtTime(f *clientmodel.Fingerprint, t time.Time) Values {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -404,7 +407,7 @@ func (s *memorySeriesStorage) GetValueAtTime(f *model.Fingerprint, t time.Time)
return series.getValueAtTime(t) return series.getValueAtTime(t)
} }
func (s *memorySeriesStorage) GetBoundaryValues(f *model.Fingerprint, i model.Interval) model.Values { func (s *memorySeriesStorage) GetBoundaryValues(f *clientmodel.Fingerprint, i Interval) Values {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -416,7 +419,7 @@ func (s *memorySeriesStorage) GetBoundaryValues(f *model.Fingerprint, i model.In
return series.getBoundaryValues(i) return series.getBoundaryValues(i)
} }
func (s *memorySeriesStorage) GetRangeValues(f *model.Fingerprint, i model.Interval) model.Values { func (s *memorySeriesStorage) GetRangeValues(f *clientmodel.Fingerprint, i Interval) Values {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -433,16 +436,16 @@ func (s *memorySeriesStorage) Close() {
s.Lock() s.Lock()
defer s.Unlock() defer s.Unlock()
s.fingerprintToSeries = map[model.Fingerprint]*stream{} s.fingerprintToSeries = map[clientmodel.Fingerprint]*stream{}
s.labelPairToFingerprints = map[model.LabelPair]model.Fingerprints{} s.labelPairToFingerprints = map[LabelPair]clientmodel.Fingerprints{}
s.labelNameToFingerprints = map[model.LabelName]model.Fingerprints{} s.labelNameToFingerprints = map[clientmodel.LabelName]clientmodel.Fingerprints{}
} }
func (s *memorySeriesStorage) GetAllValuesForLabel(labelName model.LabelName) (values model.LabelValues, err error) { func (s *memorySeriesStorage) GetAllValuesForLabel(labelName clientmodel.LabelName) (values clientmodel.LabelValues, err error) {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
valueSet := map[model.LabelValue]bool{} valueSet := map[clientmodel.LabelValue]bool{}
for _, series := range s.fingerprintToSeries { for _, series := range s.fingerprintToSeries {
if value, ok := series.metric[labelName]; ok { if value, ok := series.metric[labelName]; ok {
if !valueSet[value] { if !valueSet[value] {
@ -457,9 +460,9 @@ func (s *memorySeriesStorage) GetAllValuesForLabel(labelName model.LabelName) (v
func NewMemorySeriesStorage(o MemorySeriesOptions) *memorySeriesStorage { func NewMemorySeriesStorage(o MemorySeriesOptions) *memorySeriesStorage {
return &memorySeriesStorage{ return &memorySeriesStorage{
fingerprintToSeries: make(map[model.Fingerprint]*stream), fingerprintToSeries: make(map[clientmodel.Fingerprint]*stream),
labelPairToFingerprints: make(map[model.LabelPair]model.Fingerprints), labelPairToFingerprints: make(map[LabelPair]clientmodel.Fingerprints),
labelNameToFingerprints: make(map[model.LabelName]model.Fingerprints), labelNameToFingerprints: make(map[clientmodel.LabelName]clientmodel.Fingerprints),
wmCache: o.WatermarkCache, wmCache: o.WatermarkCache,
} }
} }

View file

@ -15,20 +15,21 @@ package metric
import ( import (
"fmt" "fmt"
"github.com/prometheus/prometheus/model"
"runtime" "runtime"
"testing" "testing"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
) )
func BenchmarkStreamAdd(b *testing.B) { func BenchmarkStreamAdd(b *testing.B) {
b.StopTimer() b.StopTimer()
s := newStream(model.Metric{}) s := newStream(clientmodel.Metric{})
times := make([]time.Time, 0, b.N) times := make([]time.Time, 0, b.N)
samples := make([]model.SampleValue, 0, b.N) samples := make([]clientmodel.SampleValue, 0, b.N)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
times = append(times, time.Date(i, 0, 0, 0, 0, 0, 0, time.UTC)) times = append(times, time.Date(i, 0, 0, 0, 0, 0, 0, time.UTC))
samples = append(samples, model.SampleValue(i)) samples = append(samples, clientmodel.SampleValue(i))
} }
b.StartTimer() b.StartTimer()
@ -50,16 +51,16 @@ func benchmarkAppendSample(b *testing.B, labels int) {
b.StopTimer() b.StopTimer()
s := NewMemorySeriesStorage(MemorySeriesOptions{}) s := NewMemorySeriesStorage(MemorySeriesOptions{})
metric := model.Metric{} metric := clientmodel.Metric{}
for i := 0; i < labels; i++ { for i := 0; i < labels; i++ {
metric[model.LabelName(fmt.Sprintf("label_%d", i))] = model.LabelValue(fmt.Sprintf("value_%d", i)) metric[clientmodel.LabelName(fmt.Sprintf("label_%d", i))] = clientmodel.LabelValue(fmt.Sprintf("value_%d", i))
} }
samples := make(model.Samples, 0, b.N) samples := make(clientmodel.Samples, 0, b.N)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
samples = append(samples, model.Sample{ samples = append(samples, &clientmodel.Sample{
Metric: metric, Metric: metric,
Value: model.SampleValue(i), Value: clientmodel.SampleValue(i),
Timestamp: time.Date(i, 0, 0, 0, 0, 0, 0, time.UTC), Timestamp: time.Date(i, 0, 0, 0, 0, 0, 0, time.UTC),
}) })
} }

View file

@ -18,8 +18,6 @@ import (
"math" "math"
"sort" "sort"
"time" "time"
"github.com/prometheus/prometheus/model"
) )
// Encapsulates a primitive query operation. // Encapsulates a primitive query operation.
@ -27,7 +25,7 @@ type op interface {
// The time at which this operation starts. // The time at which this operation starts.
StartsAt() time.Time StartsAt() time.Time
// Extract samples from stream of values and advance operation time. // Extract samples from stream of values and advance operation time.
ExtractSamples(in model.Values) (out model.Values) ExtractSamples(in Values) (out Values)
// Get current operation time or nil if no subsequent work associated with // Get current operation time or nil if no subsequent work associated with
// this operator remains. // this operator remains.
CurrentTime() *time.Time CurrentTime() *time.Time
@ -74,7 +72,7 @@ func (g *getValuesAtTimeOp) StartsAt() time.Time {
return g.time return g.time
} }
func (g *getValuesAtTimeOp) ExtractSamples(in model.Values) (out model.Values) { func (g *getValuesAtTimeOp) ExtractSamples(in Values) (out Values) {
if len(in) == 0 { if len(in) == 0 {
return return
} }
@ -101,7 +99,7 @@ func (g *getValuesAtTimeOp) GreedierThan(op op) (superior bool) {
// are adjacent to it. // are adjacent to it.
// //
// An assumption of this is that the provided samples are already sorted! // An assumption of this is that the provided samples are already sorted!
func extractValuesAroundTime(t time.Time, in model.Values) (out model.Values) { func extractValuesAroundTime(t time.Time, in Values) (out Values) {
i := sort.Search(len(in), func(i int) bool { i := sort.Search(len(in), func(i int) bool {
return !in[i].Timestamp.Before(t) return !in[i].Timestamp.Before(t)
}) })
@ -152,7 +150,7 @@ func (g *getValuesAtIntervalOp) Through() time.Time {
return g.through return g.through
} }
func (g *getValuesAtIntervalOp) ExtractSamples(in model.Values) (out model.Values) { func (g *getValuesAtIntervalOp) ExtractSamples(in Values) (out Values) {
if len(in) == 0 { if len(in) == 0 {
return return
} }
@ -212,7 +210,7 @@ func (g *getValuesAlongRangeOp) Through() time.Time {
return g.through return g.through
} }
func (g *getValuesAlongRangeOp) ExtractSamples(in model.Values) (out model.Values) { func (g *getValuesAlongRangeOp) ExtractSamples(in Values) (out Values) {
if len(in) == 0 { if len(in) == 0 {
return return
} }

View file

@ -18,7 +18,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/utility/test" "github.com/prometheus/prometheus/utility/test"
) )
@ -1203,8 +1202,8 @@ func BenchmarkOptimize(b *testing.B) {
func TestGetValuesAtTimeOp(t *testing.T) { func TestGetValuesAtTimeOp(t *testing.T) {
var scenarios = []struct { var scenarios = []struct {
op getValuesAtTimeOp op getValuesAtTimeOp
in model.Values in Values
out model.Values out Values
}{ }{
// No values. // No values.
{ {
@ -1217,13 +1216,13 @@ func TestGetValuesAtTimeOp(t *testing.T) {
op: getValuesAtTimeOp{ op: getValuesAtTimeOp{
time: testInstant, time: testInstant,
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1235,13 +1234,13 @@ func TestGetValuesAtTimeOp(t *testing.T) {
op: getValuesAtTimeOp{ op: getValuesAtTimeOp{
time: testInstant.Add(1 * time.Minute), time: testInstant.Add(1 * time.Minute),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1253,13 +1252,13 @@ func TestGetValuesAtTimeOp(t *testing.T) {
op: getValuesAtTimeOp{ op: getValuesAtTimeOp{
time: testInstant.Add(2 * time.Minute), time: testInstant.Add(2 * time.Minute),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1271,7 +1270,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
op: getValuesAtTimeOp{ op: getValuesAtTimeOp{
time: testInstant, time: testInstant,
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1281,7 +1280,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1293,7 +1292,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
op: getValuesAtTimeOp{ op: getValuesAtTimeOp{
time: testInstant.Add(1 * time.Minute), time: testInstant.Add(1 * time.Minute),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1303,7 +1302,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1315,7 +1314,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
op: getValuesAtTimeOp{ op: getValuesAtTimeOp{
time: testInstant.Add(90 * time.Second), time: testInstant.Add(90 * time.Second),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1325,7 +1324,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1341,7 +1340,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
op: getValuesAtTimeOp{ op: getValuesAtTimeOp{
time: testInstant.Add(2 * time.Minute), time: testInstant.Add(2 * time.Minute),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1351,7 +1350,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1367,7 +1366,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
op: getValuesAtTimeOp{ op: getValuesAtTimeOp{
time: testInstant.Add(3 * time.Minute), time: testInstant.Add(3 * time.Minute),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1377,7 +1376,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(2 * time.Minute), Timestamp: testInstant.Add(2 * time.Minute),
Value: 1, Value: 1,
@ -1385,6 +1384,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
}, },
}, },
} }
for i, scenario := range scenarios { for i, scenario := range scenarios {
actual := scenario.op.ExtractSamples(scenario.in) actual := scenario.op.ExtractSamples(scenario.in)
if len(actual) != len(scenario.out) { if len(actual) != len(scenario.out) {
@ -1392,7 +1392,7 @@ func TestGetValuesAtTimeOp(t *testing.T) {
t.Fatalf("%d. expected length %d, got %d", i, len(scenario.out), len(actual)) t.Fatalf("%d. expected length %d, got %d", i, len(scenario.out), len(actual))
} }
for j, out := range scenario.out { for j, out := range scenario.out {
if out != actual[j] { if !out.Equal(actual[j]) {
t.Fatalf("%d. expected output %v, got %v", i, scenario.out, actual) t.Fatalf("%d. expected output %v, got %v", i, scenario.out, actual)
} }
} }
@ -1402,8 +1402,8 @@ func TestGetValuesAtTimeOp(t *testing.T) {
func TestGetValuesAtIntervalOp(t *testing.T) { func TestGetValuesAtIntervalOp(t *testing.T) {
var scenarios = []struct { var scenarios = []struct {
op getValuesAtIntervalOp op getValuesAtIntervalOp
in model.Values in Values
out model.Values out Values
}{ }{
// No values. // No values.
{ {
@ -1420,7 +1420,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
through: testInstant.Add(1 * time.Minute), through: testInstant.Add(1 * time.Minute),
interval: 30 * time.Second, interval: 30 * time.Second,
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(2 * time.Minute), Timestamp: testInstant.Add(2 * time.Minute),
Value: 1, Value: 1,
@ -1430,7 +1430,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(2 * time.Minute), Timestamp: testInstant.Add(2 * time.Minute),
Value: 1, Value: 1,
@ -1444,7 +1444,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
through: testInstant.Add(2 * time.Minute), through: testInstant.Add(2 * time.Minute),
interval: 30 * time.Second, interval: 30 * time.Second,
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1454,7 +1454,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1472,7 +1472,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
through: testInstant.Add(2 * time.Minute), through: testInstant.Add(2 * time.Minute),
interval: 30 * time.Second, interval: 30 * time.Second,
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant, Timestamp: testInstant,
Value: 1, Value: 1,
@ -1486,7 +1486,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1504,7 +1504,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
through: testInstant.Add(3 * time.Minute), through: testInstant.Add(3 * time.Minute),
interval: 30 * time.Second, interval: 30 * time.Second,
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1514,7 +1514,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1532,7 +1532,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
through: testInstant.Add(4 * time.Minute), through: testInstant.Add(4 * time.Minute),
interval: 30 * time.Second, interval: 30 * time.Second,
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant, Timestamp: testInstant,
Value: 1, Value: 1,
@ -1550,7 +1550,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(2 * time.Minute), Timestamp: testInstant.Add(2 * time.Minute),
Value: 1, Value: 1,
@ -1568,7 +1568,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
through: testInstant.Add(3 * time.Minute), through: testInstant.Add(3 * time.Minute),
interval: 30 * time.Second, interval: 30 * time.Second,
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant, Timestamp: testInstant,
Value: 1, Value: 1,
@ -1578,7 +1578,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1596,7 +1596,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
through: testInstant.Add(4 * time.Minute), through: testInstant.Add(4 * time.Minute),
interval: 3 * time.Minute, interval: 3 * time.Minute,
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant, Timestamp: testInstant,
Value: 1, Value: 1,
@ -1614,7 +1614,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant, Timestamp: testInstant,
Value: 1, Value: 1,
@ -1647,7 +1647,7 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
} }
for j, out := range scenario.out { for j, out := range scenario.out {
if out != actual[j] { if !out.Equal(actual[j]) {
t.Fatalf("%d. expected output %v, got %v", i, scenario.out, actual) t.Fatalf("%d. expected output %v, got %v", i, scenario.out, actual)
} }
} }
@ -1657,8 +1657,8 @@ func TestGetValuesAtIntervalOp(t *testing.T) {
func TestGetValuesAlongRangeOp(t *testing.T) { func TestGetValuesAlongRangeOp(t *testing.T) {
var scenarios = []struct { var scenarios = []struct {
op getValuesAlongRangeOp op getValuesAlongRangeOp
in model.Values in Values
out model.Values out Values
}{ }{
// No values. // No values.
{ {
@ -1673,7 +1673,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
from: testInstant, from: testInstant,
through: testInstant.Add(1 * time.Minute), through: testInstant.Add(1 * time.Minute),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(2 * time.Minute), Timestamp: testInstant.Add(2 * time.Minute),
Value: 1, Value: 1,
@ -1683,7 +1683,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{}, out: Values{},
}, },
// Operator range starts before first value, ends within available values. // Operator range starts before first value, ends within available values.
{ {
@ -1691,7 +1691,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
from: testInstant, from: testInstant,
through: testInstant.Add(2 * time.Minute), through: testInstant.Add(2 * time.Minute),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1701,7 +1701,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1714,7 +1714,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
from: testInstant.Add(1 * time.Minute), from: testInstant.Add(1 * time.Minute),
through: testInstant.Add(2 * time.Minute), through: testInstant.Add(2 * time.Minute),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant, Timestamp: testInstant,
Value: 1, Value: 1,
@ -1728,7 +1728,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1741,7 +1741,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
from: testInstant, from: testInstant,
through: testInstant.Add(3 * time.Minute), through: testInstant.Add(3 * time.Minute),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1751,7 +1751,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(1 * time.Minute), Timestamp: testInstant.Add(1 * time.Minute),
Value: 1, Value: 1,
@ -1768,7 +1768,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
from: testInstant.Add(2 * time.Minute), from: testInstant.Add(2 * time.Minute),
through: testInstant.Add(4 * time.Minute), through: testInstant.Add(4 * time.Minute),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant, Timestamp: testInstant,
Value: 1, Value: 1,
@ -1786,7 +1786,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{ out: Values{
{ {
Timestamp: testInstant.Add(2 * time.Minute), Timestamp: testInstant.Add(2 * time.Minute),
Value: 1, Value: 1,
@ -1803,7 +1803,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
from: testInstant.Add(2 * time.Minute), from: testInstant.Add(2 * time.Minute),
through: testInstant.Add(3 * time.Minute), through: testInstant.Add(3 * time.Minute),
}, },
in: model.Values{ in: Values{
{ {
Timestamp: testInstant, Timestamp: testInstant,
Value: 1, Value: 1,
@ -1813,7 +1813,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
Value: 1, Value: 1,
}, },
}, },
out: model.Values{}, out: Values{},
}, },
} }
for i, scenario := range scenarios { for i, scenario := range scenarios {
@ -1823,7 +1823,7 @@ func TestGetValuesAlongRangeOp(t *testing.T) {
t.Fatalf("%d. expected length %d, got %d", i, len(scenario.out), len(actual)) t.Fatalf("%d. expected length %d, got %d", i, len(scenario.out), len(actual))
} }
for j, out := range scenario.out { for j, out := range scenario.out {
if out != actual[j] { if !out.Equal(actual[j]) {
t.Fatalf("%d. expected output %v, got %v", i, scenario.out, actual) t.Fatalf("%d. expected output %v, got %v", i, scenario.out, actual)
} }
} }

View file

@ -19,9 +19,10 @@ import (
"code.google.com/p/goprotobuf/proto" "code.google.com/p/goprotobuf/proto"
clientmodel "github.com/prometheus/client_golang/model"
dto "github.com/prometheus/prometheus/model/generated" dto "github.com/prometheus/prometheus/model/generated"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/storage/raw" "github.com/prometheus/prometheus/storage/raw"
"github.com/prometheus/prometheus/storage/raw/leveldb" "github.com/prometheus/prometheus/storage/raw/leveldb"
) )
@ -35,7 +36,7 @@ type Processor interface {
Name() string Name() string
// Signature emits a byte signature for this process for the purpose of // Signature emits a byte signature for this process for the purpose of
// remarking how far along it has been applied to the database. // remarking how far along it has been applied to the database.
Signature() (signature []byte, err error) Signature() []byte
// Apply runs this processor against the sample set. sampleIterator expects // Apply runs this processor against the sample set. sampleIterator expects
// to be pre-seeked to the initial starting position. The processor will // to be pre-seeked to the initial starting position. The processor will
// run until up until stopAt has been reached. It is imperative that the // run until up until stopAt has been reached. It is imperative that the
@ -43,7 +44,7 @@ type Processor interface {
// //
// Upon completion or error, the last time at which the processor finished // Upon completion or error, the last time at which the processor finished
// shall be emitted in addition to any errors. // shall be emitted in addition to any errors.
Apply(sampleIterator leveldb.Iterator, samplesPersistence raw.Persistence, stopAt time.Time, fingerprint *model.Fingerprint) (lastCurated time.Time, err error) Apply(sampleIterator leveldb.Iterator, samplesPersistence raw.Persistence, stopAt time.Time, fingerprint *clientmodel.Fingerprint) (lastCurated time.Time, err error)
} }
// CompactionProcessor combines sparse values in the database together such // CompactionProcessor combines sparse values in the database together such
@ -63,29 +64,30 @@ type CompactionProcessor struct {
signature []byte signature []byte
} }
func (p CompactionProcessor) Name() string { func (p *CompactionProcessor) Name() string {
return "io.prometheus.CompactionProcessorDefinition" return "io.prometheus.CompactionProcessorDefinition"
} }
func (p *CompactionProcessor) Signature() (out []byte, err error) { func (p *CompactionProcessor) Signature() []byte {
if len(p.signature) == 0 { if len(p.signature) == 0 {
out, err = proto.Marshal(&dto.CompactionProcessorDefinition{ out, err := proto.Marshal(&dto.CompactionProcessorDefinition{
MinimumGroupSize: proto.Uint32(uint32(p.MinimumGroupSize)), MinimumGroupSize: proto.Uint32(uint32(p.MinimumGroupSize)),
}) })
if err != nil {
panic(err)
}
p.signature = out p.signature = out
} }
out = p.signature return p.signature
return
} }
func (p CompactionProcessor) String() string { func (p *CompactionProcessor) String() string {
return fmt.Sprintf("compactionProcessor for minimum group size %d", p.MinimumGroupSize) return fmt.Sprintf("compactionProcessor for minimum group size %d", p.MinimumGroupSize)
} }
func (p CompactionProcessor) Apply(sampleIterator leveldb.Iterator, samplesPersistence raw.Persistence, stopAt time.Time, fingerprint *model.Fingerprint) (lastCurated time.Time, err error) { func (p *CompactionProcessor) Apply(sampleIterator leveldb.Iterator, samplesPersistence raw.Persistence, stopAt time.Time, fingerprint *clientmodel.Fingerprint) (lastCurated time.Time, err error) {
var pendingBatch raw.Batch = nil var pendingBatch raw.Batch = nil
defer func() { defer func() {
@ -95,9 +97,9 @@ func (p CompactionProcessor) Apply(sampleIterator leveldb.Iterator, samplesPersi
}() }()
var pendingMutations = 0 var pendingMutations = 0
var pendingSamples model.Values var pendingSamples Values
var sampleKey model.SampleKey var sampleKey *SampleKey
var unactedSamples model.Values var unactedSamples Values
var lastTouchedTime time.Time var lastTouchedTime time.Time
var keyDropped bool var keyDropped bool
@ -151,27 +153,36 @@ func (p CompactionProcessor) Apply(sampleIterator leveldb.Iterator, samplesPersi
case len(pendingSamples) == 0 && len(unactedSamples) >= p.MinimumGroupSize: case len(pendingSamples) == 0 && len(unactedSamples) >= p.MinimumGroupSize:
lastTouchedTime = unactedSamples[len(unactedSamples)-1].Timestamp lastTouchedTime = unactedSamples[len(unactedSamples)-1].Timestamp
unactedSamples = model.Values{} unactedSamples = Values{}
case len(pendingSamples)+len(unactedSamples) < p.MinimumGroupSize: case len(pendingSamples)+len(unactedSamples) < p.MinimumGroupSize:
if !keyDropped { if !keyDropped {
pendingBatch.Drop(sampleKey.ToDTO()) k := new(dto.SampleKey)
sampleKey.Dump(k)
pendingBatch.Drop(k)
keyDropped = true keyDropped = true
} }
pendingSamples = append(pendingSamples, unactedSamples...) pendingSamples = append(pendingSamples, unactedSamples...)
lastTouchedTime = unactedSamples[len(unactedSamples)-1].Timestamp lastTouchedTime = unactedSamples[len(unactedSamples)-1].Timestamp
unactedSamples = model.Values{} unactedSamples = Values{}
pendingMutations++ pendingMutations++
// If the number of pending writes equals the target group size // If the number of pending writes equals the target group size
case len(pendingSamples) == p.MinimumGroupSize: case len(pendingSamples) == p.MinimumGroupSize:
k := new(dto.SampleKey)
newSampleKey := pendingSamples.ToSampleKey(fingerprint) newSampleKey := pendingSamples.ToSampleKey(fingerprint)
pendingBatch.Put(newSampleKey.ToDTO(), pendingSamples.ToDTO()) newSampleKey.Dump(k)
b := new(dto.SampleValueSeries)
pendingSamples.dump(b)
pendingBatch.Put(k, b)
pendingMutations++ pendingMutations++
lastCurated = newSampleKey.FirstTimestamp.In(time.UTC) lastCurated = newSampleKey.FirstTimestamp.In(time.UTC)
if len(unactedSamples) > 0 { if len(unactedSamples) > 0 {
if !keyDropped { if !keyDropped {
pendingBatch.Drop(sampleKey.ToDTO()) sampleKey.Dump(k)
pendingBatch.Drop(k)
keyDropped = true keyDropped = true
} }
@ -182,13 +193,15 @@ func (p CompactionProcessor) Apply(sampleIterator leveldb.Iterator, samplesPersi
} else { } else {
pendingSamples = unactedSamples pendingSamples = unactedSamples
lastTouchedTime = pendingSamples[len(pendingSamples)-1].Timestamp lastTouchedTime = pendingSamples[len(pendingSamples)-1].Timestamp
unactedSamples = model.Values{} unactedSamples = Values{}
} }
} }
case len(pendingSamples)+len(unactedSamples) >= p.MinimumGroupSize: case len(pendingSamples)+len(unactedSamples) >= p.MinimumGroupSize:
if !keyDropped { if !keyDropped {
pendingBatch.Drop(sampleKey.ToDTO()) k := new(dto.SampleKey)
sampleKey.Dump(k)
pendingBatch.Drop(k)
keyDropped = true keyDropped = true
} }
remainder := p.MinimumGroupSize - len(pendingSamples) remainder := p.MinimumGroupSize - len(pendingSamples)
@ -207,9 +220,13 @@ func (p CompactionProcessor) Apply(sampleIterator leveldb.Iterator, samplesPersi
if len(unactedSamples) > 0 || len(pendingSamples) > 0 { if len(unactedSamples) > 0 || len(pendingSamples) > 0 {
pendingSamples = append(pendingSamples, unactedSamples...) pendingSamples = append(pendingSamples, unactedSamples...)
k := new(dto.SampleKey)
newSampleKey := pendingSamples.ToSampleKey(fingerprint) newSampleKey := pendingSamples.ToSampleKey(fingerprint)
pendingBatch.Put(newSampleKey.ToDTO(), pendingSamples.ToDTO()) newSampleKey.Dump(k)
pendingSamples = model.Values{} b := new(dto.SampleValueSeries)
pendingSamples.dump(b)
pendingBatch.Put(k, b)
pendingSamples = Values{}
pendingMutations++ pendingMutations++
lastCurated = newSampleKey.FirstTimestamp.In(time.UTC) lastCurated = newSampleKey.FirstTimestamp.In(time.UTC)
} }
@ -237,27 +254,29 @@ type DeletionProcessor struct {
signature []byte signature []byte
} }
func (p DeletionProcessor) Name() string { func (p *DeletionProcessor) Name() string {
return "io.prometheus.DeletionProcessorDefinition" return "io.prometheus.DeletionProcessorDefinition"
} }
func (p *DeletionProcessor) Signature() (out []byte, err error) { func (p *DeletionProcessor) Signature() []byte {
if len(p.signature) == 0 { if len(p.signature) == 0 {
out, err = proto.Marshal(&dto.DeletionProcessorDefinition{}) out, err := proto.Marshal(&dto.DeletionProcessorDefinition{})
if err != nil {
panic(err)
}
p.signature = out p.signature = out
} }
out = p.signature return p.signature
return
} }
func (p DeletionProcessor) String() string { func (p *DeletionProcessor) String() string {
return "deletionProcessor" return "deletionProcessor"
} }
func (p DeletionProcessor) Apply(sampleIterator leveldb.Iterator, samplesPersistence raw.Persistence, stopAt time.Time, fingerprint *model.Fingerprint) (lastCurated time.Time, err error) { func (p *DeletionProcessor) Apply(sampleIterator leveldb.Iterator, samplesPersistence raw.Persistence, stopAt time.Time, fingerprint *clientmodel.Fingerprint) (lastCurated time.Time, err error) {
var pendingBatch raw.Batch = nil var pendingBatch raw.Batch = nil
defer func() { defer func() {
@ -315,20 +334,28 @@ func (p DeletionProcessor) Apply(sampleIterator leveldb.Iterator, samplesPersist
pendingBatch = nil pendingBatch = nil
case !sampleKey.MayContain(stopAt): case !sampleKey.MayContain(stopAt):
pendingBatch.Drop(sampleKey.ToDTO()) k := &dto.SampleKey{}
sampleKey.Dump(k)
pendingBatch.Drop(k)
lastCurated = sampleKey.LastTimestamp lastCurated = sampleKey.LastTimestamp
sampleValues = model.Values{} sampleValues = Values{}
pendingMutations++ pendingMutations++
case sampleKey.MayContain(stopAt): case sampleKey.MayContain(stopAt):
pendingBatch.Drop(sampleKey.ToDTO()) k := &dto.SampleKey{}
sampleKey.Dump(k)
pendingBatch.Drop(k)
pendingMutations++ pendingMutations++
sampleValues = sampleValues.TruncateBefore(stopAt) sampleValues = sampleValues.TruncateBefore(stopAt)
if len(sampleValues) > 0 { if len(sampleValues) > 0 {
k := &dto.SampleKey{}
sampleKey = sampleValues.ToSampleKey(fingerprint) sampleKey = sampleValues.ToSampleKey(fingerprint)
sampleKey.Dump(k)
v := &dto.SampleValueSeries{}
sampleValues.dump(v)
lastCurated = sampleKey.FirstTimestamp lastCurated = sampleKey.FirstTimestamp
pendingBatch.Put(sampleKey.ToDTO(), sampleValues.ToDTO()) pendingBatch.Put(k, v)
pendingMutations++ pendingMutations++
} else { } else {
lastCurated = sampleKey.LastTimestamp lastCurated = sampleKey.LastTimestamp

View file

@ -20,10 +20,11 @@ import (
"code.google.com/p/goprotobuf/proto" "code.google.com/p/goprotobuf/proto"
clientmodel "github.com/prometheus/client_golang/model"
dto "github.com/prometheus/prometheus/model/generated" dto "github.com/prometheus/prometheus/model/generated"
fixture "github.com/prometheus/prometheus/storage/raw/leveldb/test" fixture "github.com/prometheus/prometheus/storage/raw/leveldb/test"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/storage/raw/leveldb" "github.com/prometheus/prometheus/storage/raw/leveldb"
) )
@ -41,7 +42,7 @@ type watermarkState struct {
type sampleGroup struct { type sampleGroup struct {
fingerprint string fingerprint string
values model.Values values Values
} }
type in struct { type in struct {
@ -59,41 +60,59 @@ type out struct {
} }
func (c curationState) Get() (key, value proto.Message) { func (c curationState) Get() (key, value proto.Message) {
signature, err := c.processor.Signature() signature := c.processor.Signature()
if err != nil { fingerprint := &clientmodel.Fingerprint{}
panic(err) fingerprint.LoadFromString(c.fingerprint)
} keyRaw := curationKey{
key = model.CurationKey{ Fingerprint: fingerprint,
Fingerprint: model.NewFingerprintFromRowKey(c.fingerprint),
ProcessorMessageRaw: signature, ProcessorMessageRaw: signature,
ProcessorMessageTypeName: c.processor.Name(), ProcessorMessageTypeName: c.processor.Name(),
IgnoreYoungerThan: c.ignoreYoungerThan, IgnoreYoungerThan: c.ignoreYoungerThan,
}.ToDTO() }
value = model.CurationRemark{ k := &dto.CurationKey{}
keyRaw.dump(k)
key = k
valueRaw := curationRemark{
LastCompletionTimestamp: c.lastCurated, LastCompletionTimestamp: c.lastCurated,
}.ToDTO() }
v := &dto.CurationValue{}
valueRaw.dump(v)
return return k, v
} }
func (w watermarkState) Get() (key, value proto.Message) { func (w watermarkState) Get() (key, value proto.Message) {
key = model.NewFingerprintFromRowKey(w.fingerprint).ToDTO() fingerprint := &clientmodel.Fingerprint{}
value = model.NewWatermarkFromTime(w.lastAppended).ToMetricHighWatermarkDTO() fingerprint.LoadFromString(w.fingerprint)
return k := &dto.Fingerprint{}
dumpFingerprint(k, fingerprint)
v := &dto.MetricHighWatermark{}
rawValue := &watermarks{
High: w.lastAppended,
}
rawValue.dump(v)
return k, v
} }
func (s sampleGroup) Get() (key, value proto.Message) { func (s sampleGroup) Get() (key, value proto.Message) {
key = model.SampleKey{ fingerprint := &clientmodel.Fingerprint{}
Fingerprint: model.NewFingerprintFromRowKey(s.fingerprint), fingerprint.LoadFromString(s.fingerprint)
keyRaw := SampleKey{
Fingerprint: fingerprint,
FirstTimestamp: s.values[0].Timestamp, FirstTimestamp: s.values[0].Timestamp,
LastTimestamp: s.values[len(s.values)-1].Timestamp, LastTimestamp: s.values[len(s.values)-1].Timestamp,
SampleCount: uint32(len(s.values)), SampleCount: uint32(len(s.values)),
}.ToDTO() }
k := &dto.SampleKey{}
keyRaw.Dump(k)
value = s.values.ToDTO() v := &dto.SampleValueSeries{}
s.values.dump(v)
return return k, v
} }
func TestCuratorCompactionProcessor(t *testing.T) { func TestCuratorCompactionProcessor(t *testing.T) {
@ -152,7 +171,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroups: fixture.Pairs{ sampleGroups: fixture.Pairs{
sampleGroup{ sampleGroup{
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 90 * time.Minute), Timestamp: testInstant.Add(-1 * 90 * time.Minute),
Value: 0, Value: 0,
@ -177,7 +196,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 65 * time.Minute), Timestamp: testInstant.Add(-1 * 65 * time.Minute),
Value: 0.25, Value: 0.25,
@ -202,7 +221,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 40 * time.Minute), Timestamp: testInstant.Add(-1 * 40 * time.Minute),
Value: 0.50, Value: 0.50,
@ -219,7 +238,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 25 * time.Minute), Timestamp: testInstant.Add(-1 * 25 * time.Minute),
Value: 0.75, Value: 0.75,
@ -228,7 +247,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 20 * time.Minute), Timestamp: testInstant.Add(-1 * 20 * time.Minute),
Value: -2, Value: -2,
@ -237,7 +256,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 15 * time.Minute), Timestamp: testInstant.Add(-1 * 15 * time.Minute),
Value: -3, Value: -3,
@ -247,7 +266,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 1 // Moved into Block 1
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 90 * time.Minute), Timestamp: testInstant.Add(-1 * 90 * time.Minute),
Value: 0, Value: 0,
@ -257,7 +276,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 1 // Moved into Block 1
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 89 * time.Minute), Timestamp: testInstant.Add(-1 * 89 * time.Minute),
Value: 1, Value: 1,
@ -267,7 +286,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 1 // Moved into Block 1
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 88 * time.Minute), Timestamp: testInstant.Add(-1 * 88 * time.Minute),
Value: 2, Value: 2,
@ -277,7 +296,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 1 // Moved into Block 1
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 87 * time.Minute), Timestamp: testInstant.Add(-1 * 87 * time.Minute),
Value: 3, Value: 3,
@ -287,7 +306,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 1 // Moved into Block 1
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 86 * time.Minute), Timestamp: testInstant.Add(-1 * 86 * time.Minute),
Value: 4, Value: 4,
@ -297,7 +316,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 2 // Moved into Block 2
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 85 * time.Minute), Timestamp: testInstant.Add(-1 * 85 * time.Minute),
Value: 5, Value: 5,
@ -307,7 +326,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 2 // Moved into Block 2
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 84 * time.Minute), Timestamp: testInstant.Add(-1 * 84 * time.Minute),
Value: 6, Value: 6,
@ -317,7 +336,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 2 // Moved into Block 2
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 83 * time.Minute), Timestamp: testInstant.Add(-1 * 83 * time.Minute),
Value: 7, Value: 7,
@ -327,7 +346,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 2 // Moved into Block 2
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 82 * time.Minute), Timestamp: testInstant.Add(-1 * 82 * time.Minute),
Value: 8, Value: 8,
@ -337,7 +356,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 2 // Moved into Block 2
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 81 * time.Minute), Timestamp: testInstant.Add(-1 * 81 * time.Minute),
Value: 9, Value: 9,
@ -347,7 +366,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 3 // Moved into Block 3
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 80 * time.Minute), Timestamp: testInstant.Add(-1 * 80 * time.Minute),
Value: 10, Value: 10,
@ -357,7 +376,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 3 // Moved into Block 3
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 79 * time.Minute), Timestamp: testInstant.Add(-1 * 79 * time.Minute),
Value: 11, Value: 11,
@ -367,7 +386,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 3 // Moved into Block 3
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 78 * time.Minute), Timestamp: testInstant.Add(-1 * 78 * time.Minute),
Value: 12, Value: 12,
@ -377,7 +396,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 3 // Moved into Block 3
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 77 * time.Minute), Timestamp: testInstant.Add(-1 * 77 * time.Minute),
Value: 13, Value: 13,
@ -387,7 +406,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Blocks 3 and 4 and 5 // Moved into Blocks 3 and 4 and 5
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
// Block 3 // Block 3
Timestamp: testInstant.Add(-1 * 76 * time.Minute), Timestamp: testInstant.Add(-1 * 76 * time.Minute),
@ -428,7 +447,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 5 // Moved into Block 5
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 69 * time.Minute), Timestamp: testInstant.Add(-1 * 69 * time.Minute),
Value: 21, Value: 21,
@ -438,7 +457,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 5 // Moved into Block 5
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 68 * time.Minute), Timestamp: testInstant.Add(-1 * 68 * time.Minute),
Value: 22, Value: 22,
@ -448,7 +467,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 5 // Moved into Block 5
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 67 * time.Minute), Timestamp: testInstant.Add(-1 * 67 * time.Minute),
Value: 23, Value: 23,
@ -458,7 +477,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 5 // Moved into Block 5
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 66 * time.Minute), Timestamp: testInstant.Add(-1 * 66 * time.Minute),
Value: 24, Value: 24,
@ -468,7 +487,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 6 // Moved into Block 6
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 65 * time.Minute), Timestamp: testInstant.Add(-1 * 65 * time.Minute),
Value: 25, Value: 25,
@ -478,7 +497,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 6 // Moved into Block 6
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 64 * time.Minute), Timestamp: testInstant.Add(-1 * 64 * time.Minute),
Value: 26, Value: 26,
@ -488,7 +507,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 6 // Moved into Block 6
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 63 * time.Minute), Timestamp: testInstant.Add(-1 * 63 * time.Minute),
Value: 27, Value: 27,
@ -498,7 +517,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 6 // Moved into Block 6
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 62 * time.Minute), Timestamp: testInstant.Add(-1 * 62 * time.Minute),
Value: 28, Value: 28,
@ -508,7 +527,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 6 // Moved into Block 6
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 61 * time.Minute), Timestamp: testInstant.Add(-1 * 61 * time.Minute),
Value: 29, Value: 29,
@ -518,7 +537,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroup{ sampleGroup{
// Moved into Block 7 // Moved into Block 7
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 60 * time.Minute), Timestamp: testInstant.Add(-1 * 60 * time.Minute),
Value: 30, Value: 30,
@ -560,7 +579,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
sampleGroups: []sampleGroup{ sampleGroups: []sampleGroup{
{ {
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 90 * time.Minute), Timestamp: testInstant.Add(-1 * 90 * time.Minute),
Value: 0, Value: 0,
@ -585,7 +604,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
{ {
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 65 * time.Minute), Timestamp: testInstant.Add(-1 * 65 * time.Minute),
Value: 0.25, Value: 0.25,
@ -610,7 +629,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
{ {
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 40 * time.Minute), Timestamp: testInstant.Add(-1 * 40 * time.Minute),
Value: 0.50, Value: 0.50,
@ -627,7 +646,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
{ {
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 25 * time.Minute), Timestamp: testInstant.Add(-1 * 25 * time.Minute),
Value: 0.75, Value: 0.75,
@ -636,7 +655,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
{ {
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 20 * time.Minute), Timestamp: testInstant.Add(-1 * 20 * time.Minute),
Value: -2, Value: -2,
@ -645,7 +664,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
{ {
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 15 * time.Minute), Timestamp: testInstant.Add(-1 * 15 * time.Minute),
Value: -3, Value: -3,
@ -655,7 +674,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
{ {
// Block 1 // Block 1
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 90 * time.Minute), Timestamp: testInstant.Add(-1 * 90 * time.Minute),
Value: 0, Value: 0,
@ -681,7 +700,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
{ {
// Block 2 // Block 2
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 85 * time.Minute), Timestamp: testInstant.Add(-1 * 85 * time.Minute),
Value: 5, Value: 5,
@ -707,7 +726,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
{ {
// Block 3 // Block 3
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 80 * time.Minute), Timestamp: testInstant.Add(-1 * 80 * time.Minute),
Value: 10, Value: 10,
@ -732,7 +751,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
{ {
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 75 * time.Minute), Timestamp: testInstant.Add(-1 * 75 * time.Minute),
Value: 15, Value: 15,
@ -757,7 +776,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
{ {
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 70 * time.Minute), Timestamp: testInstant.Add(-1 * 70 * time.Minute),
Value: 20, Value: 20,
@ -782,7 +801,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
{ {
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 65 * time.Minute), Timestamp: testInstant.Add(-1 * 65 * time.Minute),
Value: 25, Value: 25,
@ -807,7 +826,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
}, },
{ {
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 60 * time.Minute), Timestamp: testInstant.Add(-1 * 60 * time.Minute),
Value: 30, Value: 30,
@ -888,17 +907,16 @@ func TestCuratorCompactionProcessor(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("%d.%d. could not unmarshal: %s", i, j, err) t.Fatalf("%d.%d. could not unmarshal: %s", i, j, err)
} }
actualKey := &curationKey{}
actualKey.load(curationKeyDto)
actualCurationRemark := &curationRemark{}
actualCurationRemark.load(curationValueDto)
signature := expected.processor.Signature()
curationKey := model.NewCurationKeyFromDTO(curationKeyDto) expectedFingerprint := &clientmodel.Fingerprint{}
actualCurationRemark := model.NewCurationRemarkFromDTO(curationValueDto) expectedFingerprint.LoadFromString(expected.fingerprint)
signature, err := expected.processor.Signature() expectedKey := &curationKey{
if err != nil { Fingerprint: expectedFingerprint,
t.Fatal(err)
}
actualKey := curationKey
expectedKey := model.CurationKey{
Fingerprint: model.NewFingerprintFromRowKey(expected.fingerprint),
IgnoreYoungerThan: expected.ignoreYoungerThan, IgnoreYoungerThan: expected.ignoreYoungerThan,
ProcessorMessageRaw: signature, ProcessorMessageRaw: signature,
ProcessorMessageTypeName: expected.processor.Name(), ProcessorMessageTypeName: expected.processor.Name(),
@ -906,7 +924,7 @@ func TestCuratorCompactionProcessor(t *testing.T) {
if !actualKey.Equal(expectedKey) { if !actualKey.Equal(expectedKey) {
t.Fatalf("%d.%d. expected %s, got %s", i, j, expectedKey, actualKey) t.Fatalf("%d.%d. expected %s, got %s", i, j, expectedKey, actualKey)
} }
expectedCurationRemark := model.CurationRemark{ expectedCurationRemark := curationRemark{
LastCompletionTimestamp: expected.lastCurated, LastCompletionTimestamp: expected.lastCurated,
} }
if !actualCurationRemark.Equal(expectedCurationRemark) { if !actualCurationRemark.Equal(expectedCurationRemark) {
@ -938,7 +956,9 @@ func TestCuratorCompactionProcessor(t *testing.T) {
t.Fatalf("%d.%d. error %s", i, j, err) t.Fatalf("%d.%d. error %s", i, j, err)
} }
if !model.NewFingerprintFromRowKey(expected.fingerprint).Equal(sampleKey.Fingerprint) { expectedFingerprint := &clientmodel.Fingerprint{}
expectedFingerprint.LoadFromString(expected.fingerprint)
if !expectedFingerprint.Equal(sampleKey.Fingerprint) {
t.Fatalf("%d.%d. expected fingerprint %s, got %s", i, j, expected.fingerprint, sampleKey.Fingerprint) t.Fatalf("%d.%d. expected fingerprint %s, got %s", i, j, expected.fingerprint, sampleKey.Fingerprint)
} }
@ -1014,7 +1034,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
sampleGroups: fixture.Pairs{ sampleGroups: fixture.Pairs{
sampleGroup{ sampleGroup{
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 90 * time.Minute), Timestamp: testInstant.Add(-1 * 90 * time.Minute),
Value: 90, Value: 90,
@ -1027,7 +1047,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 15 * time.Minute), Timestamp: testInstant.Add(-1 * 15 * time.Minute),
Value: 15, Value: 15,
@ -1036,7 +1056,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 90 * time.Minute), Timestamp: testInstant.Add(-1 * 90 * time.Minute),
Value: 0, Value: 0,
@ -1045,7 +1065,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 89 * time.Minute), Timestamp: testInstant.Add(-1 * 89 * time.Minute),
Value: 1, Value: 1,
@ -1054,7 +1074,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 88 * time.Minute), Timestamp: testInstant.Add(-1 * 88 * time.Minute),
Value: 2, Value: 2,
@ -1063,7 +1083,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 87 * time.Minute), Timestamp: testInstant.Add(-1 * 87 * time.Minute),
Value: 3, Value: 3,
@ -1072,7 +1092,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 86 * time.Minute), Timestamp: testInstant.Add(-1 * 86 * time.Minute),
Value: 4, Value: 4,
@ -1081,7 +1101,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 85 * time.Minute), Timestamp: testInstant.Add(-1 * 85 * time.Minute),
Value: 5, Value: 5,
@ -1090,7 +1110,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 84 * time.Minute), Timestamp: testInstant.Add(-1 * 84 * time.Minute),
Value: 6, Value: 6,
@ -1099,7 +1119,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 83 * time.Minute), Timestamp: testInstant.Add(-1 * 83 * time.Minute),
Value: 7, Value: 7,
@ -1108,7 +1128,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 82 * time.Minute), Timestamp: testInstant.Add(-1 * 82 * time.Minute),
Value: 8, Value: 8,
@ -1117,7 +1137,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 81 * time.Minute), Timestamp: testInstant.Add(-1 * 81 * time.Minute),
Value: 9, Value: 9,
@ -1126,7 +1146,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 80 * time.Minute), Timestamp: testInstant.Add(-1 * 80 * time.Minute),
Value: 10, Value: 10,
@ -1135,7 +1155,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 79 * time.Minute), Timestamp: testInstant.Add(-1 * 79 * time.Minute),
Value: 11, Value: 11,
@ -1144,7 +1164,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 78 * time.Minute), Timestamp: testInstant.Add(-1 * 78 * time.Minute),
Value: 12, Value: 12,
@ -1153,7 +1173,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 77 * time.Minute), Timestamp: testInstant.Add(-1 * 77 * time.Minute),
Value: 13, Value: 13,
@ -1162,7 +1182,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 76 * time.Minute), Timestamp: testInstant.Add(-1 * 76 * time.Minute),
Value: 14, Value: 14,
@ -1195,7 +1215,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 69 * time.Minute), Timestamp: testInstant.Add(-1 * 69 * time.Minute),
Value: 21, Value: 21,
@ -1204,7 +1224,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 68 * time.Minute), Timestamp: testInstant.Add(-1 * 68 * time.Minute),
Value: 22, Value: 22,
@ -1213,7 +1233,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 67 * time.Minute), Timestamp: testInstant.Add(-1 * 67 * time.Minute),
Value: 23, Value: 23,
@ -1222,7 +1242,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 66 * time.Minute), Timestamp: testInstant.Add(-1 * 66 * time.Minute),
Value: 24, Value: 24,
@ -1231,7 +1251,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 65 * time.Minute), Timestamp: testInstant.Add(-1 * 65 * time.Minute),
Value: 25, Value: 25,
@ -1240,7 +1260,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 64 * time.Minute), Timestamp: testInstant.Add(-1 * 64 * time.Minute),
Value: 26, Value: 26,
@ -1249,7 +1269,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 63 * time.Minute), Timestamp: testInstant.Add(-1 * 63 * time.Minute),
Value: 27, Value: 27,
@ -1258,7 +1278,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 62 * time.Minute), Timestamp: testInstant.Add(-1 * 62 * time.Minute),
Value: 28, Value: 28,
@ -1267,7 +1287,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 61 * time.Minute), Timestamp: testInstant.Add(-1 * 61 * time.Minute),
Value: 29, Value: 29,
@ -1276,7 +1296,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
sampleGroup{ sampleGroup{
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 60 * time.Minute), Timestamp: testInstant.Add(-1 * 60 * time.Minute),
Value: 30, Value: 30,
@ -1307,7 +1327,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
sampleGroups: []sampleGroup{ sampleGroups: []sampleGroup{
{ {
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 30 * time.Minute), Timestamp: testInstant.Add(-1 * 30 * time.Minute),
Value: 30, Value: 30,
@ -1316,7 +1336,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
{ {
fingerprint: "0001-A-1-Z", fingerprint: "0001-A-1-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 15 * time.Minute), Timestamp: testInstant.Add(-1 * 15 * time.Minute),
Value: 15, Value: 15,
@ -1325,7 +1345,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
}, },
{ {
fingerprint: "0002-A-2-Z", fingerprint: "0002-A-2-Z",
values: model.Values{ values: Values{
{ {
Timestamp: testInstant.Add(-1 * 60 * time.Minute), Timestamp: testInstant.Add(-1 * 60 * time.Minute),
Value: 30, Value: 30,
@ -1407,16 +1427,16 @@ func TestCuratorDeletionProcessor(t *testing.T) {
t.Fatalf("%d.%d. could not unmarshal: %s", i, j, err) t.Fatalf("%d.%d. could not unmarshal: %s", i, j, err)
} }
curationKey := model.NewCurationKeyFromDTO(curationKeyDto) actualKey := &curationKey{}
actualCurationRemark := model.NewCurationRemarkFromDTO(curationValueDto) actualKey.load(curationKeyDto)
signature, err := expected.processor.Signature() actualCurationRemark := &curationRemark{}
if err != nil { actualCurationRemark.load(curationValueDto)
t.Fatal(err) signature := expected.processor.Signature()
}
actualKey := curationKey expectedFingerprint := &clientmodel.Fingerprint{}
expectedKey := model.CurationKey{ expectedFingerprint.LoadFromString(expected.fingerprint)
Fingerprint: model.NewFingerprintFromRowKey(expected.fingerprint), expectedKey := &curationKey{
Fingerprint: expectedFingerprint,
IgnoreYoungerThan: expected.ignoreYoungerThan, IgnoreYoungerThan: expected.ignoreYoungerThan,
ProcessorMessageRaw: signature, ProcessorMessageRaw: signature,
ProcessorMessageTypeName: expected.processor.Name(), ProcessorMessageTypeName: expected.processor.Name(),
@ -1424,7 +1444,7 @@ func TestCuratorDeletionProcessor(t *testing.T) {
if !actualKey.Equal(expectedKey) { if !actualKey.Equal(expectedKey) {
t.Fatalf("%d.%d. expected %s, got %s", i, j, expectedKey, actualKey) t.Fatalf("%d.%d. expected %s, got %s", i, j, expectedKey, actualKey)
} }
expectedCurationRemark := model.CurationRemark{ expectedCurationRemark := curationRemark{
LastCompletionTimestamp: expected.lastCurated, LastCompletionTimestamp: expected.lastCurated,
} }
if !actualCurationRemark.Equal(expectedCurationRemark) { if !actualCurationRemark.Equal(expectedCurationRemark) {
@ -1456,7 +1476,9 @@ func TestCuratorDeletionProcessor(t *testing.T) {
t.Fatalf("%d.%d. error %s", i, j, err) t.Fatalf("%d.%d. error %s", i, j, err)
} }
if !model.NewFingerprintFromRowKey(expected.fingerprint).Equal(sampleKey.Fingerprint) { expectedFingerprint := &clientmodel.Fingerprint{}
expectedFingerprint.LoadFromString(expected.fingerprint)
if !expectedFingerprint.Equal(sampleKey.Fingerprint) {
t.Fatalf("%d.%d. expected fingerprint %s, got %s", i, j, expected.fingerprint, sampleKey.Fingerprint) t.Fatalf("%d.%d. expected fingerprint %s, got %s", i, j, expected.fingerprint, sampleKey.Fingerprint)
} }

View file

@ -14,38 +14,40 @@
package metric package metric
import ( import (
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/utility/test"
"testing" "testing"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/utility/test"
) )
func GetFingerprintsForLabelSetUsesAndForLabelMatchingTests(p MetricPersistence, t test.Tester) { func GetFingerprintsForLabelSetUsesAndForLabelMatchingTests(p MetricPersistence, t test.Tester) {
metrics := []model.LabelSet{ metrics := []clientmodel.LabelSet{
{model.MetricNameLabel: "request_metrics_latency_equal_tallying_microseconds", "instance": "http://localhost:9090/metrics.json", "percentile": "0.010000"}, {clientmodel.MetricNameLabel: "request_metrics_latency_equal_tallying_microseconds", "instance": "http://localhost:9090/metrics.json", "percentile": "0.010000"},
{model.MetricNameLabel: "requests_metrics_latency_equal_accumulating_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"},
{model.MetricNameLabel: "requests_metrics_latency_logarithmic_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"},
{model.MetricNameLabel: "requests_metrics_latency_logarithmic_tallying_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"},
{model.MetricNameLabel: "targets_healthy_scrape_latency_ms", "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 { for _, metric := range metrics {
m := model.Metric{} m := clientmodel.Metric{}
for k, v := range metric { for k, v := range metric {
m[model.LabelName(k)] = model.LabelValue(v) m[clientmodel.LabelName(k)] = clientmodel.LabelValue(v)
} }
testAppendSample(p, model.Sample{ testAppendSample(p, &clientmodel.Sample{
Value: model.SampleValue(0.0), Value: clientmodel.SampleValue(0.0),
Timestamp: time.Now(), Timestamp: time.Now(),
Metric: m, Metric: m,
}, t) }, t)
} }
labelSet := model.LabelSet{ labelSet := clientmodel.LabelSet{
model.MetricNameLabel: "targets_healthy_scrape_latency_ms", clientmodel.MetricNameLabel: "targets_healthy_scrape_latency_ms",
"percentile": "0.010000", "percentile": "0.010000",
} }
fingerprints, err := p.GetFingerprintsForLabelSet(labelSet) fingerprints, err := p.GetFingerprintsForLabelSet(labelSet)

View file

@ -14,10 +14,12 @@
package metric package metric
import ( import (
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/utility/test"
"testing" "testing"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/utility/test"
) )
func GetValueAtTimeTests(persistenceMaker func() (MetricPersistence, test.Closer), t test.Tester) { func GetValueAtTimeTests(persistenceMaker func() (MetricPersistence, test.Closer), t test.Tester) {
@ -26,7 +28,7 @@ func GetValueAtTimeTests(persistenceMaker func() (MetricPersistence, test.Closer
month time.Month month time.Month
day int day int
hour int hour int
value model.SampleValue value clientmodel.SampleValue
} }
type input struct { type input struct {
@ -36,7 +38,7 @@ func GetValueAtTimeTests(persistenceMaker func() (MetricPersistence, test.Closer
hour int hour int
} }
type output []model.SampleValue type output []clientmodel.SampleValue
type behavior struct { type behavior struct {
name string name string
@ -320,13 +322,13 @@ func GetValueAtTimeTests(persistenceMaker func() (MetricPersistence, test.Closer
defer closer.Close() defer closer.Close()
defer p.Close() defer p.Close()
m := model.Metric{ m := clientmodel.Metric{
model.MetricNameLabel: "age_in_years", clientmodel.MetricNameLabel: "age_in_years",
} }
for _, value := range context.values { for _, value := range context.values {
testAppendSample(p, model.Sample{ testAppendSample(p, &clientmodel.Sample{
Value: model.SampleValue(value.value), Value: clientmodel.SampleValue(value.value),
Timestamp: time.Date(value.year, value.month, value.day, value.hour, 0, 0, 0, time.UTC), Timestamp: time.Date(value.year, value.month, value.day, value.hour, 0, 0, 0, time.UTC),
Metric: m, Metric: m,
}, t) }, t)
@ -335,8 +337,9 @@ func GetValueAtTimeTests(persistenceMaker func() (MetricPersistence, test.Closer
for j, behavior := range context.behaviors { for j, behavior := range context.behaviors {
input := behavior.input input := behavior.input
time := time.Date(input.year, input.month, input.day, input.hour, 0, 0, 0, time.UTC) time := time.Date(input.year, input.month, input.day, input.hour, 0, 0, 0, time.UTC)
fingerprint := &clientmodel.Fingerprint{}
actual := p.GetValueAtTime(model.NewFingerprintFromMetric(m), time) fingerprint.LoadFromMetric(m)
actual := p.GetValueAtTime(fingerprint, time)
if len(behavior.output) != len(actual) { if len(behavior.output) != len(actual) {
t.Fatalf("%d.%d(%s.%s). Expected %d samples but got: %v\n", i, j, context.name, behavior.name, len(behavior.output), actual) t.Fatalf("%d.%d(%s.%s). Expected %d samples but got: %v\n", i, j, context.name, behavior.name, len(behavior.output), actual)
@ -358,7 +361,7 @@ func GetRangeValuesTests(persistenceMaker func() (MetricPersistence, test.Closer
month time.Month month time.Month
day int day int
hour int hour int
value model.SampleValue value clientmodel.SampleValue
} }
type input struct { type input struct {
@ -377,7 +380,7 @@ func GetRangeValuesTests(persistenceMaker func() (MetricPersistence, test.Closer
month time.Month month time.Month
day int day int
hour int hour int
value model.SampleValue value clientmodel.SampleValue
} }
type behavior struct { type behavior struct {
@ -811,13 +814,13 @@ func GetRangeValuesTests(persistenceMaker func() (MetricPersistence, test.Closer
defer closer.Close() defer closer.Close()
defer p.Close() defer p.Close()
m := model.Metric{ m := clientmodel.Metric{
model.MetricNameLabel: "age_in_years", clientmodel.MetricNameLabel: "age_in_years",
} }
for _, value := range context.values { for _, value := range context.values {
testAppendSample(p, model.Sample{ testAppendSample(p, &clientmodel.Sample{
Value: model.SampleValue(value.value), Value: clientmodel.SampleValue(value.value),
Timestamp: time.Date(value.year, value.month, value.day, value.hour, 0, 0, 0, time.UTC), Timestamp: time.Date(value.year, value.month, value.day, value.hour, 0, 0, 0, time.UTC),
Metric: m, Metric: m,
}, t) }, t)
@ -827,14 +830,15 @@ func GetRangeValuesTests(persistenceMaker func() (MetricPersistence, test.Closer
input := behavior.input input := behavior.input
open := time.Date(input.openYear, input.openMonth, input.openDay, input.openHour, 0, 0, 0, time.UTC) 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) end := time.Date(input.endYear, input.endMonth, input.endDay, input.endHour, 0, 0, 0, time.UTC)
in := model.Interval{ in := Interval{
OldestInclusive: open, OldestInclusive: open,
NewestInclusive: end, NewestInclusive: end,
} }
actualValues := model.Values{} actualValues := Values{}
expectedValues := []output{} expectedValues := []output{}
fp := model.NewFingerprintFromMetric(m) fp := &clientmodel.Fingerprint{}
fp.LoadFromMetric(m)
if onlyBoundaries { if onlyBoundaries {
actualValues = p.GetBoundaryValues(fp, in) actualValues = p.GetBoundaryValues(fp, in)
l := len(behavior.output) l := len(behavior.output)
@ -865,7 +869,7 @@ func GetRangeValuesTests(persistenceMaker func() (MetricPersistence, test.Closer
for k, actual := range actualValues { for k, actual := range actualValues {
expected := expectedValues[k] expected := expectedValues[k]
if actual.Value != model.SampleValue(expected.value) { if actual.Value != clientmodel.SampleValue(expected.value) {
t.Fatalf("%d.%d.%d(%s). Expected %v but got: %v\n", i, j, k, behavior.name, expected.value, actual.Value) t.Fatalf("%d.%d.%d(%s). Expected %v but got: %v\n", i, j, k, behavior.name, expected.value, actual.Value)
} }

170
storage/metric/sample.go Normal file
View file

@ -0,0 +1,170 @@
package metric
import (
"bytes"
"fmt"
"sort"
"time"
"code.google.com/p/goprotobuf/proto"
clientmodel "github.com/prometheus/client_golang/model"
dto "github.com/prometheus/prometheus/model/generated"
)
func (s SamplePair) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("{\"Value\": \"%f\", \"Timestamp\": %d}", s.Value, s.Timestamp.Unix())), nil
}
type SamplePair struct {
Value clientmodel.SampleValue
Timestamp time.Time
}
func (s *SamplePair) Equal(o *SamplePair) bool {
if s == o {
return true
}
return s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp)
}
func (s *SamplePair) dump(d *dto.SampleValueSeries_Value) {
d.Reset()
d.Timestamp = proto.Int64(s.Timestamp.Unix())
d.Value = proto.Float64(float64(s.Value))
}
func (s *SamplePair) String() string {
return fmt.Sprintf("SamplePair at %s of %s", s.Timestamp, s.Value)
}
type Values []*SamplePair
func (v Values) Len() int {
return len(v)
}
func (v Values) Less(i, j int) bool {
return v[i].Timestamp.Before(v[j].Timestamp)
}
func (v Values) Swap(i, j int) {
v[i], v[j] = v[j], v[i]
}
func (v Values) Equal(o Values) bool {
if len(v) != len(o) {
return false
}
for i, expected := range v {
if !expected.Equal(o[i]) {
return false
}
}
return true
}
// FirstTimeAfter indicates whether the first sample of a set is after a given
// timestamp.
func (v Values) FirstTimeAfter(t time.Time) bool {
return v[0].Timestamp.After(t)
}
// LastTimeBefore indicates whether the last sample of a set is before a given
// timestamp.
func (v Values) LastTimeBefore(t time.Time) bool {
return v[len(v)-1].Timestamp.Before(t)
}
// InsideInterval indicates whether a given range of sorted values could contain
// a value for a given time.
func (v Values) InsideInterval(t time.Time) bool {
switch {
case v.Len() == 0:
return false
case t.Before(v[0].Timestamp):
return false
case !v[v.Len()-1].Timestamp.Before(t):
return false
default:
return true
}
}
// TruncateBefore returns a subslice of the original such that extraneous
// samples in the collection that occur before the provided time are
// dropped. The original slice is not mutated
func (v Values) TruncateBefore(t time.Time) Values {
index := sort.Search(len(v), func(i int) bool {
timestamp := v[i].Timestamp
return !timestamp.Before(t)
})
return v[index:]
}
func (v Values) dump(d *dto.SampleValueSeries) {
d.Reset()
for _, value := range v {
element := &dto.SampleValueSeries_Value{}
value.dump(element)
d.Value = append(d.Value, element)
}
}
func (v Values) ToSampleKey(f *clientmodel.Fingerprint) *SampleKey {
return &SampleKey{
Fingerprint: f,
FirstTimestamp: v[0].Timestamp,
LastTimestamp: v[len(v)-1].Timestamp,
SampleCount: uint32(len(v)),
}
}
func (v Values) String() string {
buffer := bytes.Buffer{}
fmt.Fprintf(&buffer, "[")
for i, value := range v {
fmt.Fprintf(&buffer, "%d. %s", i, value)
if i != len(v)-1 {
fmt.Fprintf(&buffer, "\n")
}
}
fmt.Fprintf(&buffer, "]")
return buffer.String()
}
func NewValuesFromDTO(d *dto.SampleValueSeries) Values {
// BUG(matt): Incogruent from the other load/dump API types, but much more
// performant.
v := make(Values, 0, len(d.Value))
for _, value := range d.Value {
v = append(v, &SamplePair{
Timestamp: time.Unix(value.GetTimestamp(), 0).UTC(),
Value: clientmodel.SampleValue(value.GetValue()),
})
}
return v
}
type SampleSet struct {
Metric clientmodel.Metric
Values Values
}
type Interval struct {
OldestInclusive time.Time
NewestInclusive time.Time
}

View file

@ -11,20 +11,25 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package model package metric
import ( import (
"code.google.com/p/goprotobuf/proto"
"fmt" "fmt"
"github.com/prometheus/prometheus/coding/indexable"
dto "github.com/prometheus/prometheus/model/generated"
"time" "time"
"code.google.com/p/goprotobuf/proto"
clientmodel "github.com/prometheus/client_golang/model"
dto "github.com/prometheus/prometheus/model/generated"
"github.com/prometheus/prometheus/coding/indexable"
) )
// SampleKey models the business logic around the data-transfer object // SampleKey models the business logic around the data-transfer object
// SampleKey. // SampleKey.
type SampleKey struct { type SampleKey struct {
Fingerprint *Fingerprint Fingerprint *clientmodel.Fingerprint
FirstTimestamp time.Time FirstTimestamp time.Time
LastTimestamp time.Time LastTimestamp time.Time
SampleCount uint32 SampleCount uint32
@ -33,7 +38,7 @@ type SampleKey struct {
// MayContain indicates whether the given SampleKey could potentially contain a // MayContain indicates whether the given SampleKey could potentially contain a
// value at the provided time. Even if true is emitted, that does not mean a // value at the provided time. Even if true is emitted, that does not mean a
// satisfactory value, in fact, exists. // satisfactory value, in fact, exists.
func (s SampleKey) MayContain(t time.Time) bool { func (s *SampleKey) MayContain(t time.Time) bool {
switch { switch {
case t.Before(s.FirstTimestamp): case t.Before(s.FirstTimestamp):
return false return false
@ -45,40 +50,39 @@ func (s SampleKey) MayContain(t time.Time) bool {
} }
// ToDTO converts this SampleKey into a DTO for use in serialization purposes. // ToDTO converts this SampleKey into a DTO for use in serialization purposes.
func (s SampleKey) ToDTO() (out *dto.SampleKey) { func (s *SampleKey) Dump(d *dto.SampleKey) {
out = &dto.SampleKey{ d.Reset()
Fingerprint: s.Fingerprint.ToDTO(), fp := &dto.Fingerprint{}
Timestamp: indexable.EncodeTime(s.FirstTimestamp), dumpFingerprint(fp, s.Fingerprint)
LastTimestamp: proto.Int64(s.LastTimestamp.Unix()),
SampleCount: proto.Uint32(s.SampleCount),
}
return d.Fingerprint = fp
d.Timestamp = indexable.EncodeTime(s.FirstTimestamp)
d.LastTimestamp = proto.Int64(s.LastTimestamp.Unix())
d.SampleCount = proto.Uint32(s.SampleCount)
} }
// ToPartialDTO converts this SampleKey into a DTO that is only suitable for // ToPartialDTO converts this SampleKey into a DTO that is only suitable for
// database exploration purposes for a given (Fingerprint, First Sample Time) // database exploration purposes for a given (Fingerprint, First Sample Time)
// tuple. // tuple.
func (s SampleKey) ToPartialDTO(out *dto.SampleKey) { func (s *SampleKey) FOOdumpPartial(d *dto.SampleKey) {
out = &dto.SampleKey{ d.Reset()
Fingerprint: s.Fingerprint.ToDTO(),
Timestamp: indexable.EncodeTime(s.FirstTimestamp),
}
return f := &dto.Fingerprint{}
dumpFingerprint(f, s.Fingerprint)
d.Fingerprint = f
d.Timestamp = indexable.EncodeTime(s.FirstTimestamp)
} }
func (s SampleKey) String() string { func (s *SampleKey) String() string {
return fmt.Sprintf("SampleKey for %s at %s to %s with %d values.", s.Fingerprint, s.FirstTimestamp, s.LastTimestamp, s.SampleCount) return fmt.Sprintf("SampleKey for %s at %s to %s with %d values.", s.Fingerprint, s.FirstTimestamp, s.LastTimestamp, s.SampleCount)
} }
// NewSampleKeyFromDTO builds a new SampleKey from a provided data-transfer func (s *SampleKey) Load(d *dto.SampleKey) {
// object. f := &clientmodel.Fingerprint{}
func NewSampleKeyFromDTO(dto *dto.SampleKey) SampleKey { loadFingerprint(f, d.GetFingerprint())
return SampleKey{ s.Fingerprint = f
Fingerprint: NewFingerprintFromDTO(dto.Fingerprint), s.FirstTimestamp = indexable.DecodeTime(d.Timestamp)
FirstTimestamp: indexable.DecodeTime(dto.Timestamp), s.LastTimestamp = time.Unix(d.GetLastTimestamp(), 0).UTC()
LastTimestamp: time.Unix(*dto.LastTimestamp, 0).UTC(), s.SampleCount = d.GetSampleCount()
SampleCount: *dto.SampleCount,
}
} }

View file

@ -16,12 +16,13 @@ package metric
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/prometheus/prometheus/model"
clientmodel "github.com/prometheus/client_golang/model"
) )
// scanJob models a range of queries. // scanJob models a range of queries.
type scanJob struct { type scanJob struct {
fingerprint *model.Fingerprint fingerprint *clientmodel.Fingerprint
operations ops operations ops
} }

View file

@ -15,22 +15,23 @@ package metric
import ( import (
"fmt" "fmt"
"github.com/prometheus/prometheus/coding"
"github.com/prometheus/prometheus/coding/indexable"
"github.com/prometheus/prometheus/model"
dto "github.com/prometheus/prometheus/model/generated"
"github.com/prometheus/prometheus/utility/test"
"math" "math"
"math/rand" "math/rand"
"sort" "sort"
"testing" "testing"
"testing/quick" "testing/quick"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
dto "github.com/prometheus/prometheus/model/generated"
"github.com/prometheus/prometheus/coding"
"github.com/prometheus/prometheus/coding/indexable"
"github.com/prometheus/prometheus/utility/test"
) )
const ( const stochasticMaximumVariance = 8
stochasticMaximumVariance = 8
)
func BasicLifecycleTests(p MetricPersistence, t test.Tester) { func BasicLifecycleTests(p MetricPersistence, t test.Tester) {
if p == nil { if p == nil {
@ -41,10 +42,10 @@ func BasicLifecycleTests(p MetricPersistence, t test.Tester) {
func ReadEmptyTests(p MetricPersistence, t test.Tester) { func ReadEmptyTests(p MetricPersistence, t test.Tester) {
hasLabelPair := func(x int) (success bool) { hasLabelPair := func(x int) (success bool) {
name := model.LabelName(string(x)) name := clientmodel.LabelName(string(x))
value := model.LabelValue(string(x)) value := clientmodel.LabelValue(string(x))
labelSet := model.LabelSet{ labelSet := clientmodel.LabelSet{
name: value, name: value,
} }
@ -69,7 +70,7 @@ func ReadEmptyTests(p MetricPersistence, t test.Tester) {
} }
hasLabelName := func(x int) (success bool) { hasLabelName := func(x int) (success bool) {
labelName := model.LabelName(string(x)) labelName := clientmodel.LabelName(string(x))
fingerprints, err := p.GetFingerprintsForLabelName(labelName) fingerprints, err := p.GetFingerprintsForLabelName(labelName)
if err != nil { if err != nil {
@ -94,13 +95,13 @@ func ReadEmptyTests(p MetricPersistence, t test.Tester) {
func AppendSampleAsPureSparseAppendTests(p MetricPersistence, t test.Tester) { func AppendSampleAsPureSparseAppendTests(p MetricPersistence, t test.Tester) {
appendSample := func(x int) (success bool) { appendSample := func(x int) (success bool) {
v := model.SampleValue(x) v := clientmodel.SampleValue(x)
ts := time.Unix(int64(x), int64(x)) ts := time.Unix(int64(x), int64(x))
labelName := model.LabelName(x) labelName := clientmodel.LabelName(x)
labelValue := model.LabelValue(x) labelValue := clientmodel.LabelValue(x)
l := model.Metric{labelName: labelValue} l := clientmodel.Metric{labelName: labelValue}
sample := model.Sample{ sample := &clientmodel.Sample{
Value: v, Value: v,
Timestamp: ts, Timestamp: ts,
Metric: l, Metric: l,
@ -123,13 +124,13 @@ func AppendSampleAsPureSparseAppendTests(p MetricPersistence, t test.Tester) {
func AppendSampleAsSparseAppendWithReadsTests(p MetricPersistence, t test.Tester) { func AppendSampleAsSparseAppendWithReadsTests(p MetricPersistence, t test.Tester) {
appendSample := func(x int) (success bool) { appendSample := func(x int) (success bool) {
v := model.SampleValue(x) v := clientmodel.SampleValue(x)
ts := time.Unix(int64(x), int64(x)) ts := time.Unix(int64(x), int64(x))
labelName := model.LabelName(x) labelName := clientmodel.LabelName(x)
labelValue := model.LabelValue(x) labelValue := clientmodel.LabelValue(x)
l := model.Metric{labelName: labelValue} l := clientmodel.Metric{labelName: labelValue}
sample := model.Sample{ sample := &clientmodel.Sample{
Value: v, Value: v,
Timestamp: ts, Timestamp: ts,
Metric: l, Metric: l,
@ -151,7 +152,7 @@ func AppendSampleAsSparseAppendWithReadsTests(p MetricPersistence, t test.Tester
return return
} }
fingerprints, err = p.GetFingerprintsForLabelSet(model.LabelSet{ fingerprints, err = p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
labelName: labelValue, labelName: labelValue,
}) })
if err != nil { if err != nil {
@ -173,10 +174,10 @@ func AppendSampleAsSparseAppendWithReadsTests(p MetricPersistence, t test.Tester
func AppendSampleAsPureSingleEntityAppendTests(p MetricPersistence, t test.Tester) { func AppendSampleAsPureSingleEntityAppendTests(p MetricPersistence, t test.Tester) {
appendSample := func(x int) bool { appendSample := func(x int) bool {
sample := model.Sample{ sample := &clientmodel.Sample{
Value: model.SampleValue(x), Value: clientmodel.SampleValue(x),
Timestamp: time.Unix(int64(x), 0), Timestamp: time.Unix(int64(x), 0),
Metric: model.Metric{model.MetricNameLabel: "my_metric"}, Metric: clientmodel.Metric{clientmodel.MetricNameLabel: "my_metric"},
} }
err := p.AppendSample(sample) err := p.AppendSample(sample)
@ -189,9 +190,11 @@ func AppendSampleAsPureSingleEntityAppendTests(p MetricPersistence, t test.Teste
} }
} }
func levelDBGetRangeValues(l *LevelDBMetricPersistence, fp *model.Fingerprint, i model.Interval) (samples model.Values, err error) { func levelDBGetRangeValues(l *LevelDBMetricPersistence, fp *clientmodel.Fingerprint, i Interval) (samples Values, err error) {
fpDto := &dto.Fingerprint{}
dumpFingerprint(fpDto, fp)
k := &dto.SampleKey{ k := &dto.SampleKey{
Fingerprint: fp.ToDTO(), Fingerprint: fpDto,
Timestamp: indexable.EncodeTime(i.OldestInclusive), Timestamp: indexable.EncodeTime(i.OldestInclusive),
} }
@ -258,23 +261,23 @@ func StochasticTests(persistenceMaker func() (MetricPersistence, test.Closer), t
metricNewestSample := map[int]int64{} metricNewestSample := map[int]int64{}
for metricIndex := 0; metricIndex < numberOfMetrics; metricIndex++ { for metricIndex := 0; metricIndex < numberOfMetrics; metricIndex++ {
sample := model.Sample{ sample := &clientmodel.Sample{
Metric: model.Metric{}, Metric: clientmodel.Metric{},
} }
v := model.LabelValue(fmt.Sprintf("metric_index_%d", metricIndex)) v := clientmodel.LabelValue(fmt.Sprintf("metric_index_%d", metricIndex))
sample.Metric[model.MetricNameLabel] = v sample.Metric[clientmodel.MetricNameLabel] = v
for sharedLabelIndex := 0; sharedLabelIndex < numberOfSharedLabels; sharedLabelIndex++ { for sharedLabelIndex := 0; sharedLabelIndex < numberOfSharedLabels; sharedLabelIndex++ {
l := model.LabelName(fmt.Sprintf("shared_label_%d", sharedLabelIndex)) l := clientmodel.LabelName(fmt.Sprintf("shared_label_%d", sharedLabelIndex))
v := model.LabelValue(fmt.Sprintf("label_%d", sharedLabelIndex)) v := clientmodel.LabelValue(fmt.Sprintf("label_%d", sharedLabelIndex))
sample.Metric[l] = v sample.Metric[l] = v
} }
for unsharedLabelIndex := 0; unsharedLabelIndex < numberOfUnsharedLabels; unsharedLabelIndex++ { for unsharedLabelIndex := 0; unsharedLabelIndex < numberOfUnsharedLabels; unsharedLabelIndex++ {
l := model.LabelName(fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, unsharedLabelIndex)) l := clientmodel.LabelName(fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, unsharedLabelIndex))
v := model.LabelValue(fmt.Sprintf("private_label_%d", unsharedLabelIndex)) v := clientmodel.LabelValue(fmt.Sprintf("private_label_%d", unsharedLabelIndex))
sample.Metric[l] = v sample.Metric[l] = v
} }
@ -316,7 +319,7 @@ func StochasticTests(persistenceMaker func() (MetricPersistence, test.Closer), t
for sampleIndex := 0; sampleIndex < numberOfSamples; sampleIndex++ { for sampleIndex := 0; sampleIndex < numberOfSamples; sampleIndex++ {
sample.Timestamp = sortedTimestamps[sampleIndex] sample.Timestamp = sortedTimestamps[sampleIndex]
sample.Value = model.SampleValue(sampleIndex) sample.Value = clientmodel.SampleValue(sampleIndex)
err := p.AppendSample(sample) err := p.AppendSample(sample)
@ -330,8 +333,8 @@ func StochasticTests(persistenceMaker func() (MetricPersistence, test.Closer), t
metricNewestSample[metricIndex] = newestSample metricNewestSample[metricIndex] = newestSample
for sharedLabelIndex := 0; sharedLabelIndex < numberOfSharedLabels; sharedLabelIndex++ { for sharedLabelIndex := 0; sharedLabelIndex < numberOfSharedLabels; sharedLabelIndex++ {
labelPair := model.LabelSet{ labelPair := clientmodel.LabelSet{
model.LabelName(fmt.Sprintf("shared_label_%d", sharedLabelIndex)): model.LabelValue(fmt.Sprintf("label_%d", sharedLabelIndex)), clientmodel.LabelName(fmt.Sprintf("shared_label_%d", sharedLabelIndex)): clientmodel.LabelValue(fmt.Sprintf("label_%d", sharedLabelIndex)),
} }
fingerprints, err := p.GetFingerprintsForLabelSet(labelPair) fingerprints, err := p.GetFingerprintsForLabelSet(labelPair)
@ -344,7 +347,7 @@ func StochasticTests(persistenceMaker func() (MetricPersistence, test.Closer), t
return return
} }
labelName := model.LabelName(fmt.Sprintf("shared_label_%d", sharedLabelIndex)) labelName := clientmodel.LabelName(fmt.Sprintf("shared_label_%d", sharedLabelIndex))
fingerprints, err = p.GetFingerprintsForLabelName(labelName) fingerprints, err = p.GetFingerprintsForLabelName(labelName)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -358,7 +361,7 @@ func StochasticTests(persistenceMaker func() (MetricPersistence, test.Closer), t
} }
for sharedIndex := 0; sharedIndex < numberOfSharedLabels; sharedIndex++ { for sharedIndex := 0; sharedIndex < numberOfSharedLabels; sharedIndex++ {
labelName := model.LabelName(fmt.Sprintf("shared_label_%d", sharedIndex)) labelName := clientmodel.LabelName(fmt.Sprintf("shared_label_%d", sharedIndex))
fingerprints, err := p.GetFingerprintsForLabelName(labelName) fingerprints, err := p.GetFingerprintsForLabelName(labelName)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -373,9 +376,9 @@ func StochasticTests(persistenceMaker func() (MetricPersistence, test.Closer), t
for metricIndex := 0; metricIndex < numberOfMetrics; metricIndex++ { for metricIndex := 0; metricIndex < numberOfMetrics; metricIndex++ {
for unsharedLabelIndex := 0; unsharedLabelIndex < numberOfUnsharedLabels; unsharedLabelIndex++ { for unsharedLabelIndex := 0; unsharedLabelIndex < numberOfUnsharedLabels; unsharedLabelIndex++ {
labelName := model.LabelName(fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, unsharedLabelIndex)) labelName := clientmodel.LabelName(fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, unsharedLabelIndex))
labelValue := model.LabelValue(fmt.Sprintf("private_label_%d", unsharedLabelIndex)) labelValue := clientmodel.LabelValue(fmt.Sprintf("private_label_%d", unsharedLabelIndex))
labelSet := model.LabelSet{ labelSet := clientmodel.LabelSet{
labelName: labelValue, labelName: labelValue,
} }
@ -400,19 +403,19 @@ func StochasticTests(persistenceMaker func() (MetricPersistence, test.Closer), t
} }
} }
metric := model.Metric{} metric := clientmodel.Metric{}
metric[model.MetricNameLabel] = model.LabelValue(fmt.Sprintf("metric_index_%d", metricIndex)) metric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(fmt.Sprintf("metric_index_%d", metricIndex))
for i := 0; i < numberOfSharedLabels; i++ { for i := 0; i < numberOfSharedLabels; i++ {
l := model.LabelName(fmt.Sprintf("shared_label_%d", i)) l := clientmodel.LabelName(fmt.Sprintf("shared_label_%d", i))
v := model.LabelValue(fmt.Sprintf("label_%d", i)) v := clientmodel.LabelValue(fmt.Sprintf("label_%d", i))
metric[l] = v metric[l] = v
} }
for i := 0; i < numberOfUnsharedLabels; i++ { for i := 0; i < numberOfUnsharedLabels; i++ {
l := model.LabelName(fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, i)) l := clientmodel.LabelName(fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, i))
v := model.LabelValue(fmt.Sprintf("private_label_%d", i)) v := clientmodel.LabelValue(fmt.Sprintf("private_label_%d", i))
metric[l] = v metric[l] = v
} }
@ -461,13 +464,14 @@ func StochasticTests(persistenceMaker func() (MetricPersistence, test.Closer), t
begin, end = second, first begin, end = second, first
} }
interval := model.Interval{ interval := Interval{
OldestInclusive: time.Unix(begin, 0), OldestInclusive: time.Unix(begin, 0),
NewestInclusive: time.Unix(end, 0), NewestInclusive: time.Unix(end, 0),
} }
samples := model.Values{} samples := Values{}
fp := model.NewFingerprintFromMetric(metric) fp := &clientmodel.Fingerprint{}
fp.LoadFromMetric(metric)
switch persistence := p.(type) { switch persistence := p.(type) {
case *LevelDBMetricPersistence: case *LevelDBMetricPersistence:
var err error var err error

View file

@ -17,19 +17,20 @@ import (
"fmt" "fmt"
"log" "log"
"sort" "sort"
"sync"
"time" "time"
dto "github.com/prometheus/prometheus/model/generated" dto "github.com/prometheus/prometheus/model/generated"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/coding" "github.com/prometheus/prometheus/coding"
"github.com/prometheus/prometheus/coding/indexable" "github.com/prometheus/prometheus/coding/indexable"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/stats" "github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/raw/leveldb" "github.com/prometheus/prometheus/storage/raw/leveldb"
"sync"
) )
type chunk model.Values type chunk Values
// TruncateBefore returns a subslice of the original such that extraneous // TruncateBefore returns a subslice of the original such that extraneous
// samples in the collection that occur before the provided time are // samples in the collection that occur before the provided time are
@ -78,7 +79,7 @@ type TieredStorage struct {
// BUG(matt): This introduces a Law of Demeter violation. Ugh. // BUG(matt): This introduces a Law of Demeter violation. Ugh.
DiskStorage *LevelDBMetricPersistence DiskStorage *LevelDBMetricPersistence
appendToDiskQueue chan model.Samples appendToDiskQueue chan clientmodel.Samples
memoryArena *memorySeriesStorage memoryArena *memorySeriesStorage
memoryTTL time.Duration memoryTTL time.Duration
@ -120,7 +121,7 @@ func NewTieredStorage(appendToDiskQueueDepth, viewQueueDepth uint, flushMemoryIn
memOptions := MemorySeriesOptions{WatermarkCache: wmCache} memOptions := MemorySeriesOptions{WatermarkCache: wmCache}
s := &TieredStorage{ s := &TieredStorage{
appendToDiskQueue: make(chan model.Samples, appendToDiskQueueDepth), appendToDiskQueue: make(chan clientmodel.Samples, appendToDiskQueueDepth),
DiskStorage: diskStorage, DiskStorage: diskStorage,
draining: make(chan chan<- bool), draining: make(chan chan<- bool),
flushMemoryInterval: flushMemoryInterval, flushMemoryInterval: flushMemoryInterval,
@ -145,7 +146,7 @@ func NewTieredStorage(appendToDiskQueueDepth, viewQueueDepth uint, flushMemoryIn
} }
// Enqueues Samples for storage. // Enqueues Samples for storage.
func (t *TieredStorage) AppendSamples(samples model.Samples) (err error) { func (t *TieredStorage) AppendSamples(samples clientmodel.Samples) (err error) {
t.mu.RLock() t.mu.RLock()
defer t.mu.RUnlock() defer t.mu.RUnlock()
if t.state != tieredStorageServing { if t.state != tieredStorageServing {
@ -170,6 +171,8 @@ func (t *TieredStorage) drain(drained chan<- bool) {
panic("Illegal State: Supplemental drain requested.") panic("Illegal State: Supplemental drain requested.")
} }
t.state = tieredStorageDraining
log.Println("Triggering drain...") log.Println("Triggering drain...")
t.draining <- (drained) t.draining <- (drained)
} }
@ -269,7 +272,7 @@ func (t *TieredStorage) flushMemory(ttl time.Duration) {
queueLength := len(t.appendToDiskQueue) queueLength := len(t.appendToDiskQueue)
if queueLength > 0 { if queueLength > 0 {
samples := model.Samples{} samples := clientmodel.Samples{}
for i := 0; i < queueLength; i++ { for i := 0; i < queueLength; i++ {
chunk := <-t.appendToDiskQueue chunk := <-t.appendToDiskQueue
samples = append(samples, chunk...) samples = append(samples, chunk...)
@ -286,6 +289,10 @@ func (t *TieredStorage) Close() {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()
t.close()
}
func (t *TieredStorage) close() {
if t.state == tieredStorageStopping { if t.state == tieredStorageStopping {
panic("Illegal State: Attempted to restop TieredStorage.") panic("Illegal State: Attempted to restop TieredStorage.")
} }
@ -305,7 +312,7 @@ func (t *TieredStorage) Close() {
t.state = tieredStorageStopping t.state = tieredStorageStopping
} }
func (t *TieredStorage) seriesTooOld(f *model.Fingerprint, i time.Time) (bool, error) { func (t *TieredStorage) seriesTooOld(f *clientmodel.Fingerprint, i time.Time) (bool, error) {
// BUG(julius): Make this configurable by query layer. // BUG(julius): Make this configurable by query layer.
i = i.Add(-stalenessLimit) i = i.Add(-stalenessLimit)
@ -315,21 +322,23 @@ func (t *TieredStorage) seriesTooOld(f *model.Fingerprint, i time.Time) (bool, e
samples := t.memoryArena.CloneSamples(f) samples := t.memoryArena.CloneSamples(f)
if len(samples) > 0 { if len(samples) > 0 {
newest := samples[len(samples)-1].Timestamp newest := samples[len(samples)-1].Timestamp
t.wmCache.Set(f, &Watermarks{High: newest}) t.wmCache.Set(f, &watermarks{High: newest})
return newest.Before(i), nil return newest.Before(i), nil
} }
} }
value := &dto.MetricHighWatermark{} value := &dto.MetricHighWatermark{}
diskHit, err := t.DiskStorage.MetricHighWatermarks.Get(f.ToDTO(), value) k := &dto.Fingerprint{}
dumpFingerprint(k, f)
diskHit, err := t.DiskStorage.MetricHighWatermarks.Get(k, value)
if err != nil { if err != nil {
return false, err return false, err
} }
if diskHit { if diskHit {
wmTime := time.Unix(*value.Timestamp, 0).UTC() wmTime := time.Unix(*value.Timestamp, 0).UTC()
t.wmCache.Set(f, &Watermarks{High: wmTime}) t.wmCache.Set(f, &watermarks{High: wmTime})
return wmTime.Before(i), nil return wmTime.Before(i), nil
} }
@ -454,7 +463,7 @@ func (t *TieredStorage) renderView(viewJob viewJob) {
} }
// For each op, extract all needed data from the current chunk. // For each op, extract all needed data from the current chunk.
out := model.Values{} out := Values{}
for _, op := range standingOps { for _, op := range standingOps {
if op.CurrentTime().After(targetTime) { if op.CurrentTime().After(targetTime) {
break break
@ -463,7 +472,7 @@ func (t *TieredStorage) renderView(viewJob viewJob) {
currentChunk = currentChunk.TruncateBefore(*(op.CurrentTime())) currentChunk = currentChunk.TruncateBefore(*(op.CurrentTime()))
for op.CurrentTime() != nil && !op.CurrentTime().After(targetTime) { for op.CurrentTime() != nil && !op.CurrentTime().After(targetTime) {
out = op.ExtractSamples(model.Values(currentChunk)) out = op.ExtractSamples(Values(currentChunk))
// Append the extracted samples to the materialized view. // Append the extracted samples to the materialized view.
view.appendSamples(scanJob.fingerprint, out) view.appendSamples(scanJob.fingerprint, out)
@ -500,14 +509,15 @@ func (t *TieredStorage) renderView(viewJob viewJob) {
return return
} }
func (t *TieredStorage) loadChunkAroundTime(iterator leveldb.Iterator, frontier *seriesFrontier, fingerprint *model.Fingerprint, ts time.Time) (chunk model.Values) { func (t *TieredStorage) loadChunkAroundTime(iterator leveldb.Iterator, frontier *seriesFrontier, fingerprint *clientmodel.Fingerprint, ts time.Time) (chunk Values) {
var (
targetKey = &dto.SampleKey{ fd := &dto.Fingerprint{}
Fingerprint: fingerprint.ToDTO(), dumpFingerprint(fd, fingerprint)
} targetKey := &dto.SampleKey{
foundKey model.SampleKey Fingerprint: fd,
foundValues model.Values }
) var foundKey *SampleKey
var foundValues Values
// Limit the target key to be within the series' keyspace. // Limit the target key to be within the series' keyspace.
if ts.After(frontier.lastSupertime) { if ts.After(frontier.lastSupertime) {
@ -577,7 +587,7 @@ func (t *TieredStorage) loadChunkAroundTime(iterator leveldb.Iterator, frontier
} }
// Get all label values that are associated with the provided label name. // Get all label values that are associated with the provided label name.
func (t *TieredStorage) GetAllValuesForLabel(labelName model.LabelName) (model.LabelValues, error) { func (t *TieredStorage) GetAllValuesForLabel(labelName clientmodel.LabelName) (clientmodel.LabelValues, error) {
t.mu.RLock() t.mu.RLock()
defer t.mu.RUnlock() defer t.mu.RUnlock()
@ -594,8 +604,8 @@ func (t *TieredStorage) GetAllValuesForLabel(labelName model.LabelName) (model.L
return nil, err return nil, err
} }
valueSet := map[model.LabelValue]bool{} valueSet := map[clientmodel.LabelValue]bool{}
values := model.LabelValues{} values := clientmodel.LabelValues{}
for _, value := range append(diskValues, memoryValues...) { for _, value := range append(diskValues, memoryValues...) {
if !valueSet[value] { if !valueSet[value] {
values = append(values, value) values = append(values, value)
@ -608,7 +618,7 @@ func (t *TieredStorage) GetAllValuesForLabel(labelName model.LabelName) (model.L
// Get all of the metric fingerprints that are associated with the provided // Get all of the metric fingerprints that are associated with the provided
// label set. // label set.
func (t *TieredStorage) GetFingerprintsForLabelSet(labelSet model.LabelSet) (model.Fingerprints, error) { func (t *TieredStorage) GetFingerprintsForLabelSet(labelSet clientmodel.LabelSet) (clientmodel.Fingerprints, error) {
t.mu.RLock() t.mu.RLock()
defer t.mu.RUnlock() defer t.mu.RUnlock()
@ -624,11 +634,11 @@ func (t *TieredStorage) GetFingerprintsForLabelSet(labelSet model.LabelSet) (mod
if err != nil { if err != nil {
return nil, err return nil, err
} }
fingerprintSet := map[model.Fingerprint]bool{} fingerprintSet := map[clientmodel.Fingerprint]bool{}
for _, fingerprint := range append(memFingerprints, diskFingerprints...) { for _, fingerprint := range append(memFingerprints, diskFingerprints...) {
fingerprintSet[*fingerprint] = true fingerprintSet[*fingerprint] = true
} }
fingerprints := model.Fingerprints{} fingerprints := clientmodel.Fingerprints{}
for fingerprint := range fingerprintSet { for fingerprint := range fingerprintSet {
fpCopy := fingerprint fpCopy := fingerprint
fingerprints = append(fingerprints, &fpCopy) fingerprints = append(fingerprints, &fpCopy)
@ -638,7 +648,7 @@ func (t *TieredStorage) GetFingerprintsForLabelSet(labelSet model.LabelSet) (mod
} }
// Get the metric associated with the provided fingerprint. // Get the metric associated with the provided fingerprint.
func (t *TieredStorage) GetMetricForFingerprint(f *model.Fingerprint) (model.Metric, error) { func (t *TieredStorage) GetMetricForFingerprint(f *clientmodel.Fingerprint) (clientmodel.Metric, error) {
t.mu.RLock() t.mu.RLock()
defer t.mu.RUnlock() defer t.mu.RUnlock()

View file

@ -14,19 +14,21 @@
package metric package metric
import ( import (
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/utility/test"
"sort" "sort"
"testing" "testing"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/utility/test"
) )
func buildSamples(from, to time.Time, interval time.Duration, m model.Metric) (v []model.Sample) { func buildSamples(from, to time.Time, interval time.Duration, m clientmodel.Metric) (v clientmodel.Samples) {
i := model.SampleValue(0) i := clientmodel.SampleValue(0)
for from.Before(to) { for from.Before(to) {
v = append(v, model.Sample{ v = append(v, &clientmodel.Sample{
Metric: m, Metric: m,
Value: i, Value: i,
Timestamp: from, Timestamp: from,
@ -47,16 +49,17 @@ func testMakeView(t test.Tester, flushToDisk bool) {
} }
type out struct { type out struct {
atTime []model.Values atTime []Values
atInterval []model.Values atInterval []Values
alongRange []model.Values alongRange []Values
} }
metric := clientmodel.Metric{clientmodel.MetricNameLabel: "request_count"}
fingerprint := &clientmodel.Fingerprint{}
fingerprint.LoadFromMetric(metric)
var ( var (
instant = time.Date(1984, 3, 30, 0, 0, 0, 0, time.Local) instant = time.Date(1984, 3, 30, 0, 0, 0, 0, time.Local)
metric = model.Metric{model.MetricNameLabel: "request_count"} scenarios = []struct {
fingerprint = *model.NewFingerprintFromMetric(metric) data clientmodel.Samples
scenarios = []struct {
data []model.Sample
in in in in
out out out out
}{ }{
@ -70,12 +73,12 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
}, },
out: out{ out: out{
atTime: []model.Values{{}}, atTime: []Values{{}},
}, },
}, },
// Single sample, query asks for exact sample time. // Single sample, query asks for exact sample time.
{ {
data: []model.Sample{ data: clientmodel.Samples{
{ {
Metric: metric, Metric: metric,
Value: 0, Value: 0,
@ -90,7 +93,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
}, },
out: out{ out: out{
atTime: []model.Values{ atTime: []Values{
{ {
{ {
Timestamp: instant, Timestamp: instant,
@ -102,7 +105,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
// Single sample, query time before the sample. // Single sample, query time before the sample.
{ {
data: []model.Sample{ data: clientmodel.Samples{
{ {
Metric: metric, Metric: metric,
Value: 0, Value: 0,
@ -122,7 +125,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
}, },
out: out{ out: out{
atTime: []model.Values{ atTime: []Values{
{ {
{ {
Timestamp: instant.Add(time.Second), Timestamp: instant.Add(time.Second),
@ -134,7 +137,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
// Single sample, query time after the sample. // Single sample, query time after the sample.
{ {
data: []model.Sample{ data: clientmodel.Samples{
{ {
Metric: metric, Metric: metric,
Value: 0, Value: 0,
@ -149,7 +152,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
}, },
out: out{ out: out{
atTime: []model.Values{ atTime: []Values{
{ {
{ {
Timestamp: instant, Timestamp: instant,
@ -161,7 +164,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
// Two samples, query asks for first sample time. // Two samples, query asks for first sample time.
{ {
data: []model.Sample{ data: clientmodel.Samples{
{ {
Metric: metric, Metric: metric,
Value: 0, Value: 0,
@ -181,7 +184,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
}, },
out: out{ out: out{
atTime: []model.Values{ atTime: []Values{
{ {
{ {
Timestamp: instant, Timestamp: instant,
@ -193,7 +196,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
// Three samples, query asks for second sample time. // Three samples, query asks for second sample time.
{ {
data: []model.Sample{ data: clientmodel.Samples{
{ {
Metric: metric, Metric: metric,
Value: 0, Value: 0,
@ -218,7 +221,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
}, },
out: out{ out: out{
atTime: []model.Values{ atTime: []Values{
{ {
{ {
Timestamp: instant.Add(time.Second), Timestamp: instant.Add(time.Second),
@ -230,7 +233,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
// Three samples, query asks for time between first and second samples. // Three samples, query asks for time between first and second samples.
{ {
data: []model.Sample{ data: clientmodel.Samples{
{ {
Metric: metric, Metric: metric,
Value: 0, Value: 0,
@ -255,7 +258,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
}, },
out: out{ out: out{
atTime: []model.Values{ atTime: []Values{
{ {
{ {
Timestamp: instant, Timestamp: instant,
@ -271,7 +274,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
// Three samples, query asks for time between second and third samples. // Three samples, query asks for time between second and third samples.
{ {
data: []model.Sample{ data: clientmodel.Samples{
{ {
Metric: metric, Metric: metric,
Value: 0, Value: 0,
@ -296,7 +299,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
}, },
out: out{ out: out{
atTime: []model.Values{ atTime: []Values{
{ {
{ {
Timestamp: instant.Add(time.Second * 2), Timestamp: instant.Add(time.Second * 2),
@ -321,7 +324,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
}, },
}, },
out: out{ out: out{
atTime: []model.Values{ atTime: []Values{
{ {
{ {
Timestamp: instant.Add(time.Second * time.Duration(*leveldbChunkSize/2)), Timestamp: instant.Add(time.Second * time.Duration(*leveldbChunkSize/2)),
@ -371,7 +374,7 @@ func testMakeView(t test.Tester, flushToDisk bool) {
} }
for j, atTime := range scenario.in.atTime { for j, atTime := range scenario.in.atTime {
actual := v.GetValueAtTime(&fingerprint, atTime.time) actual := v.GetValueAtTime(fingerprint, atTime.time)
if len(actual) != len(scenario.out.atTime[j]) { 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)) t.Fatalf("%d.%d. expected %d output, got %d", i, j, len(scenario.out.atTime[j]), len(actual))
@ -475,8 +478,8 @@ func TestGetAllValuesForLabel(t *testing.T) {
for i, scenario := range scenarios { for i, scenario := range scenarios {
tiered, closer := NewTestTieredStorage(t) tiered, closer := NewTestTieredStorage(t)
for j, metric := range scenario.in { for j, metric := range scenario.in {
sample := model.Sample{ sample := &clientmodel.Sample{
Metric: model.Metric{model.MetricNameLabel: model.LabelValue(metric.metricName)}, Metric: clientmodel.Metric{clientmodel.MetricNameLabel: clientmodel.LabelValue(metric.metricName)},
} }
if metric.appendToMemory { if metric.appendToMemory {
if err := tiered.memoryArena.AppendSample(sample); err != nil { if err := tiered.memoryArena.AppendSample(sample); err != nil {
@ -489,7 +492,7 @@ func TestGetAllValuesForLabel(t *testing.T) {
} }
} }
} }
metricNames, err := tiered.GetAllValuesForLabel(model.MetricNameLabel) metricNames, err := tiered.GetAllValuesForLabel(clientmodel.MetricNameLabel)
closer.Close() closer.Close()
if err != nil { if err != nil {
t.Fatalf("%d. Error getting metric names: %s", i, err) t.Fatalf("%d. Error getting metric names: %s", i, err)
@ -510,11 +513,11 @@ func TestGetAllValuesForLabel(t *testing.T) {
func TestGetFingerprintsForLabelSet(t *testing.T) { func TestGetFingerprintsForLabelSet(t *testing.T) {
tiered, closer := NewTestTieredStorage(t) tiered, closer := NewTestTieredStorage(t)
defer closer.Close() defer closer.Close()
memorySample := model.Sample{ memorySample := &clientmodel.Sample{
Metric: model.Metric{model.MetricNameLabel: "http_requests", "method": "/foo"}, Metric: clientmodel.Metric{clientmodel.MetricNameLabel: "http_requests", "method": "/foo"},
} }
diskSample := model.Sample{ diskSample := &clientmodel.Sample{
Metric: model.Metric{model.MetricNameLabel: "http_requests", "method": "/bar"}, Metric: clientmodel.Metric{clientmodel.MetricNameLabel: "http_requests", "method": "/bar"},
} }
if err := tiered.memoryArena.AppendSample(memorySample); err != nil { if err := tiered.memoryArena.AppendSample(memorySample); err != nil {
t.Fatalf("Failed to add fixture data: %s", err) t.Fatalf("Failed to add fixture data: %s", err)
@ -525,33 +528,33 @@ func TestGetFingerprintsForLabelSet(t *testing.T) {
tiered.Flush() tiered.Flush()
scenarios := []struct { scenarios := []struct {
labels model.LabelSet labels clientmodel.LabelSet
fpCount int fpCount int
}{ }{
{ {
labels: model.LabelSet{}, labels: clientmodel.LabelSet{},
fpCount: 0, fpCount: 0,
}, { }, {
labels: model.LabelSet{ labels: clientmodel.LabelSet{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
}, },
fpCount: 2, fpCount: 2,
}, { }, {
labels: model.LabelSet{ labels: clientmodel.LabelSet{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
"method": "/foo", "method": "/foo",
}, },
fpCount: 1, fpCount: 1,
}, { }, {
labels: model.LabelSet{ labels: clientmodel.LabelSet{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
"method": "/bar", "method": "/bar",
}, },
fpCount: 1, fpCount: 1,
}, { }, {
labels: model.LabelSet{ labels: clientmodel.LabelSet{
model.MetricNameLabel: "http_requests", clientmodel.MetricNameLabel: "http_requests",
"method": "/baz", "method": "/baz",
}, },
fpCount: 0, fpCount: 0,
}, },
@ -570,18 +573,18 @@ func TestGetFingerprintsForLabelSet(t *testing.T) {
func testTruncateBefore(t test.Tester) { func testTruncateBefore(t test.Tester) {
type in struct { type in struct {
values model.Values values Values
time time.Time time time.Time
} }
instant := time.Now() instant := time.Now()
var scenarios = []struct { var scenarios = []struct {
in in in in
out model.Values out Values
}{ }{
{ {
in: in{ in: in{
time: instant, time: instant,
values: model.Values{ values: Values{
{ {
Value: 0, Value: 0,
Timestamp: instant, Timestamp: instant,
@ -604,7 +607,7 @@ func testTruncateBefore(t test.Tester) {
}, },
}, },
}, },
out: model.Values{ out: Values{
{ {
Value: 0, Value: 0,
Timestamp: instant, Timestamp: instant,
@ -630,7 +633,7 @@ func testTruncateBefore(t test.Tester) {
{ {
in: in{ in: in{
time: instant.Add(2 * time.Second), time: instant.Add(2 * time.Second),
values: model.Values{ values: Values{
{ {
Value: 0, Value: 0,
Timestamp: instant, Timestamp: instant,
@ -653,7 +656,7 @@ func testTruncateBefore(t test.Tester) {
}, },
}, },
}, },
out: model.Values{ out: Values{
{ {
Value: 1, Value: 1,
Timestamp: instant.Add(time.Second), Timestamp: instant.Add(time.Second),
@ -675,7 +678,7 @@ func testTruncateBefore(t test.Tester) {
{ {
in: in{ in: in{
time: instant.Add(5 * time.Second), time: instant.Add(5 * time.Second),
values: model.Values{ values: Values{
{ {
Value: 0, Value: 0,
Timestamp: instant, Timestamp: instant,
@ -698,7 +701,7 @@ func testTruncateBefore(t test.Tester) {
}, },
}, },
}, },
out: model.Values{ out: Values{
// Preserve the last value in case it needs to be used for the next set. // Preserve the last value in case it needs to be used for the next set.
{ {
Value: 4, Value: 4,

View file

@ -17,7 +17,7 @@ import (
"sort" "sort"
"time" "time"
"github.com/prometheus/prometheus/model" clientmodel "github.com/prometheus/client_golang/model"
) )
var ( var (
@ -30,56 +30,56 @@ var (
// Represents the summation of all datastore queries that shall be performed to // Represents the summation of all datastore queries that shall be performed to
// extract values. Each operation mutates the state of the builder. // extract values. Each operation mutates the state of the builder.
type ViewRequestBuilder interface { type ViewRequestBuilder interface {
GetMetricAtTime(fingerprint model.Fingerprint, time time.Time) GetMetricAtTime(fingerprint *clientmodel.Fingerprint, time time.Time)
GetMetricAtInterval(fingerprint model.Fingerprint, from, through time.Time, interval time.Duration) GetMetricAtInterval(fingerprint *clientmodel.Fingerprint, from, through time.Time, interval time.Duration)
GetMetricRange(fingerprint model.Fingerprint, from, through time.Time) GetMetricRange(fingerprint *clientmodel.Fingerprint, from, through time.Time)
ScanJobs() scanJobs ScanJobs() scanJobs
} }
// Contains the various unoptimized requests for data. // Contains the various unoptimized requests for data.
type viewRequestBuilder struct { type viewRequestBuilder struct {
operations map[model.Fingerprint]ops operations map[clientmodel.Fingerprint]ops
} }
// Furnishes a ViewRequestBuilder for remarking what types of queries to perform. // Furnishes a ViewRequestBuilder for remarking what types of queries to perform.
func NewViewRequestBuilder() viewRequestBuilder { func NewViewRequestBuilder() viewRequestBuilder {
return viewRequestBuilder{ return viewRequestBuilder{
operations: make(map[model.Fingerprint]ops), operations: make(map[clientmodel.Fingerprint]ops),
} }
} }
// Gets for the given Fingerprint either the value at that time if there is an // Gets for the given Fingerprint either the value at that time if there is an
// match or the one or two values adjacent thereto. // match or the one or two values adjacent thereto.
func (v viewRequestBuilder) GetMetricAtTime(fingerprint model.Fingerprint, time time.Time) { func (v viewRequestBuilder) GetMetricAtTime(fingerprint *clientmodel.Fingerprint, time time.Time) {
ops := v.operations[fingerprint] ops := v.operations[*fingerprint]
ops = append(ops, &getValuesAtTimeOp{ ops = append(ops, &getValuesAtTimeOp{
time: time, time: time,
}) })
v.operations[fingerprint] = ops v.operations[*fingerprint] = ops
} }
// Gets for the given Fingerprint either the value at that interval from From // Gets for the given Fingerprint either the value at that interval from From
// through Through if there is an match or the one or two values adjacent // through Through if there is an match or the one or two values adjacent
// for each point. // for each point.
func (v viewRequestBuilder) GetMetricAtInterval(fingerprint model.Fingerprint, from, through time.Time, interval time.Duration) { func (v viewRequestBuilder) GetMetricAtInterval(fingerprint *clientmodel.Fingerprint, from, through time.Time, interval time.Duration) {
ops := v.operations[fingerprint] ops := v.operations[*fingerprint]
ops = append(ops, &getValuesAtIntervalOp{ ops = append(ops, &getValuesAtIntervalOp{
from: from, from: from,
through: through, through: through,
interval: interval, interval: interval,
}) })
v.operations[fingerprint] = ops v.operations[*fingerprint] = ops
} }
// Gets for the given Fingerprint either the values that occur inclusively from // Gets for the given Fingerprint either the values that occur inclusively from
// From through Through. // From through Through.
func (v viewRequestBuilder) GetMetricRange(fingerprint model.Fingerprint, from, through time.Time) { func (v viewRequestBuilder) GetMetricRange(fingerprint *clientmodel.Fingerprint, from, through time.Time) {
ops := v.operations[fingerprint] ops := v.operations[*fingerprint]
ops = append(ops, &getValuesAlongRangeOp{ ops = append(ops, &getValuesAlongRangeOp{
from: from, from: from,
through: through, through: through,
}) })
v.operations[fingerprint] = ops v.operations[*fingerprint] = ops
} }
// Emits the optimized scans that will occur in the data store. This // Emits the optimized scans that will occur in the data store. This
@ -106,7 +106,7 @@ type view struct {
*memorySeriesStorage *memorySeriesStorage
} }
func (v view) appendSamples(fingerprint *model.Fingerprint, samples model.Values) { func (v view) appendSamples(fingerprint *clientmodel.Fingerprint, samples Values) {
v.memorySeriesStorage.appendSamplesWithoutIndexing(fingerprint, samples) v.memorySeriesStorage.appendSamplesWithoutIndexing(fingerprint, samples)
} }

View file

@ -14,10 +14,12 @@
package metric package metric
import ( import (
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/utility/test"
"testing" "testing"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/utility/test"
) )
func testBuilder(t test.Tester) { func testBuilder(t test.Tester) {
@ -140,33 +142,36 @@ func testBuilder(t test.Tester) {
for i, scenario := range scenarios { for i, scenario := range scenarios {
builder := viewRequestBuilder{ builder := viewRequestBuilder{
operations: map[model.Fingerprint]ops{}, operations: map[clientmodel.Fingerprint]ops{},
} }
for _, atTime := range scenario.in.atTimes { for _, atTime := range scenario.in.atTimes {
fingerprint := *model.NewFingerprintFromRowKey(atTime.fingerprint) fingerprint := &clientmodel.Fingerprint{}
fingerprint.LoadFromString(atTime.fingerprint)
builder.GetMetricAtTime(fingerprint, atTime.time) builder.GetMetricAtTime(fingerprint, atTime.time)
} }
for _, atInterval := range scenario.in.atIntervals { for _, atInterval := range scenario.in.atIntervals {
fingerprint := *model.NewFingerprintFromRowKey(atInterval.fingerprint) fingerprint := &clientmodel.Fingerprint{}
fingerprint.LoadFromString(atInterval.fingerprint)
builder.GetMetricAtInterval(fingerprint, atInterval.from, atInterval.through, atInterval.interval) builder.GetMetricAtInterval(fingerprint, atInterval.from, atInterval.through, atInterval.interval)
} }
for _, atRange := range scenario.in.atRanges { for _, atRange := range scenario.in.atRanges {
fingerprint := *model.NewFingerprintFromRowKey(atRange.fingerprint) fingerprint := &clientmodel.Fingerprint{}
fingerprint.LoadFromString(atRange.fingerprint)
builder.GetMetricRange(fingerprint, atRange.from, atRange.through) builder.GetMetricRange(fingerprint, atRange.from, atRange.through)
} }
jobs := builder.ScanJobs() jobs := builder.ScanJobs()
if len(scenario.out) != len(jobs) { if len(scenario.out) != len(jobs) {
t.Fatalf("%d. expected job length of %d, got %d\n", i, len(scenario.out), len(jobs)) t.Fatalf("%d. expected job length of %d, got %d", i, len(scenario.out), len(jobs))
} }
for j, job := range scenario.out { for j, job := range scenario.out {
if jobs[j].fingerprint.ToRowKey() != job.fingerprint { if jobs[j].fingerprint.String() != job.fingerprint {
t.Fatalf("%d.%d. expected fingerprint %s, got %s\n", i, j, job.fingerprint, jobs[j].fingerprint.ToRowKey()) t.Fatalf("%d.%d. expected fingerprint %s, got %s", i, j, job.fingerprint, jobs[j].fingerprint)
} }
} }
} }

View file

@ -18,10 +18,14 @@ import (
"sync" "sync"
"time" "time"
"github.com/prometheus/prometheus/model" "code.google.com/p/goprotobuf/proto"
clientmodel "github.com/prometheus/client_golang/model"
dto "github.com/prometheus/prometheus/model/generated"
) )
// unsafe.Sizeof(Watermarks{}) // unsafe.Sizeof(watermarks{})
const elementSize = 24 const elementSize = 24
type Bytes uint64 type Bytes uint64
@ -32,32 +36,42 @@ type WatermarkCache struct {
mu sync.Mutex mu sync.Mutex
list *list.List list *list.List
table map[model.Fingerprint]*list.Element table map[clientmodel.Fingerprint]*list.Element
size Bytes size Bytes
allowance Bytes allowance Bytes
} }
type Watermarks struct { type watermarks struct {
High time.Time High time.Time
} }
func (w *watermarks) load(d *dto.MetricHighWatermark) {
w.High = time.Unix(d.GetTimestamp(), 0).UTC()
}
func (w *watermarks) dump(d *dto.MetricHighWatermark) {
d.Reset()
d.Timestamp = proto.Int64(w.High.Unix())
}
type entry struct { type entry struct {
fingerprint *model.Fingerprint fingerprint *clientmodel.Fingerprint
watermarks *Watermarks watermarks *watermarks
accessed time.Time accessed time.Time
} }
func NewWatermarkCache(allowance Bytes) *WatermarkCache { func NewWatermarkCache(allowance Bytes) *WatermarkCache {
return &WatermarkCache{ return &WatermarkCache{
list: list.New(), list: list.New(),
table: map[model.Fingerprint]*list.Element{}, table: map[clientmodel.Fingerprint]*list.Element{},
allowance: allowance, allowance: allowance,
} }
} }
func (lru *WatermarkCache) Get(f *model.Fingerprint) (v *Watermarks, ok bool) { func (lru *WatermarkCache) Get(f *clientmodel.Fingerprint) (v *watermarks, ok bool) {
lru.mu.Lock() lru.mu.Lock()
defer lru.mu.Unlock() defer lru.mu.Unlock()
@ -71,7 +85,7 @@ func (lru *WatermarkCache) Get(f *model.Fingerprint) (v *Watermarks, ok bool) {
return element.Value.(*entry).watermarks, true return element.Value.(*entry).watermarks, true
} }
func (lru *WatermarkCache) Set(f *model.Fingerprint, w *Watermarks) { func (lru *WatermarkCache) Set(f *clientmodel.Fingerprint, w *watermarks) {
lru.mu.Lock() lru.mu.Lock()
defer lru.mu.Unlock() defer lru.mu.Unlock()
@ -82,7 +96,7 @@ func (lru *WatermarkCache) Set(f *model.Fingerprint, w *Watermarks) {
} }
} }
func (lru *WatermarkCache) SetIfAbsent(f *model.Fingerprint, w *Watermarks) { func (lru *WatermarkCache) SetIfAbsent(f *clientmodel.Fingerprint, w *watermarks) {
lru.mu.Lock() lru.mu.Lock()
defer lru.mu.Unlock() defer lru.mu.Unlock()
@ -93,7 +107,7 @@ func (lru *WatermarkCache) SetIfAbsent(f *model.Fingerprint, w *Watermarks) {
} }
} }
func (lru *WatermarkCache) Delete(f *model.Fingerprint) bool { func (lru *WatermarkCache) Delete(f *clientmodel.Fingerprint) bool {
lru.mu.Lock() lru.mu.Lock()
defer lru.mu.Unlock() defer lru.mu.Unlock()
@ -114,11 +128,11 @@ func (lru *WatermarkCache) Clear() {
defer lru.mu.Unlock() defer lru.mu.Unlock()
lru.list.Init() lru.list.Init()
lru.table = map[model.Fingerprint]*list.Element{} lru.table = map[clientmodel.Fingerprint]*list.Element{}
lru.size = 0 lru.size = 0
} }
func (lru *WatermarkCache) updateInplace(e *list.Element, w *Watermarks) { func (lru *WatermarkCache) updateInplace(e *list.Element, w *watermarks) {
e.Value.(*entry).watermarks = w e.Value.(*entry).watermarks = w
lru.moveToFront(e) lru.moveToFront(e)
lru.checkCapacity() lru.checkCapacity()
@ -129,7 +143,7 @@ func (lru *WatermarkCache) moveToFront(e *list.Element) {
e.Value.(*entry).accessed = time.Now() e.Value.(*entry).accessed = time.Now()
} }
func (lru *WatermarkCache) addNew(f *model.Fingerprint, w *Watermarks) { func (lru *WatermarkCache) addNew(f *clientmodel.Fingerprint, w *watermarks) {
lru.table[*f] = lru.list.PushFront(&entry{ lru.table[*f] = lru.list.PushFront(&entry{
fingerprint: f, fingerprint: f,
watermarks: w, watermarks: w,

View file

@ -14,10 +14,11 @@
package leveldb package leveldb
import ( import (
"github.com/prometheus/prometheus/storage/raw"
"testing" "testing"
"github.com/prometheus/prometheus/storage/raw"
) )
func TestInterfaceAdherence(t *testing.T) { func TestInterfaceAdherence(t *testing.T) {
var _ raw.Persistence = &LevelDBPersistence{} var _ raw.Persistence = new(LevelDBPersistence)
} }

View file

@ -30,7 +30,6 @@ import (
dto "github.com/prometheus/prometheus/model/generated" dto "github.com/prometheus/prometheus/model/generated"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/storage/metric"
) )
@ -43,33 +42,36 @@ type SamplesDumper struct {
*csv.Writer *csv.Writer
} }
func (d SamplesDumper) DecodeKey(in interface{}) (interface{}, error) { func (d *SamplesDumper) DecodeKey(in interface{}) (interface{}, error) {
key := &dto.SampleKey{} key := &dto.SampleKey{}
err := proto.Unmarshal(in.([]byte), key) err := proto.Unmarshal(in.([]byte), key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.NewSampleKeyFromDTO(key), nil sampleKey := &metric.SampleKey{}
sampleKey.Load(key)
return sampleKey, nil
} }
func (d SamplesDumper) DecodeValue(in interface{}) (interface{}, error) { func (d *SamplesDumper) DecodeValue(in interface{}) (interface{}, error) {
values := &dto.SampleValueSeries{} values := &dto.SampleValueSeries{}
err := proto.Unmarshal(in.([]byte), values) err := proto.Unmarshal(in.([]byte), values)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.NewValuesFromDTO(values), nil return metric.NewValuesFromDTO(values), nil
} }
func (d SamplesDumper) Filter(_, _ interface{}) storage.FilterResult { func (d *SamplesDumper) Filter(_, _ interface{}) storage.FilterResult {
return storage.ACCEPT return storage.ACCEPT
} }
func (d SamplesDumper) Operate(key, value interface{}) *storage.OperatorError { func (d *SamplesDumper) Operate(key, value interface{}) *storage.OperatorError {
sampleKey := key.(model.SampleKey) sampleKey := key.(*metric.SampleKey)
for i, sample := range value.(model.Values) { for i, sample := range value.(metric.Values) {
d.Write([]string{ d.Write([]string{
sampleKey.Fingerprint.String(), sampleKey.Fingerprint.String(),
strconv.FormatInt(sampleKey.FirstTimestamp.Unix(), 10), strconv.FormatInt(sampleKey.FirstTimestamp.Unix(), 10),
@ -102,7 +104,10 @@ func main() {
} }
defer persistence.Close() defer persistence.Close()
dumper := SamplesDumper{csv.NewWriter(os.Stdout)} dumper := &SamplesDumper{
csv.NewWriter(os.Stdout),
}
entire, err := persistence.MetricSamples.ForEach(dumper, dumper, dumper) entire, err := persistence.MetricSamples.ForEach(dumper, dumper, dumper)
if err != nil { if err != nil {
log.Fatalf("Error dumping samples: %s", err) log.Fatalf("Error dumping samples: %s", err)

View file

@ -14,17 +14,20 @@
package api package api
import ( import (
"code.google.com/p/gorest"
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/rules"
"github.com/prometheus/prometheus/rules/ast"
"github.com/prometheus/prometheus/stats"
"log" "log"
"net/http" "net/http"
"sort" "sort"
"time" "time"
"code.google.com/p/gorest"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/rules"
"github.com/prometheus/prometheus/rules/ast"
"github.com/prometheus/prometheus/stats"
) )
func (serv MetricsService) setAccessControlHeaders(rb *gorest.ResponseBuilder) { func (serv MetricsService) setAccessControlHeaders(rb *gorest.ResponseBuilder) {
@ -114,7 +117,7 @@ func (serv MetricsService) QueryRange(expr string, end int64, duration int64, st
} }
func (serv MetricsService) Metrics() string { func (serv MetricsService) Metrics() string {
metricNames, err := serv.Storage.GetAllValuesForLabel(model.MetricNameLabel) metricNames, err := serv.Storage.GetAllValuesForLabel(clientmodel.MetricNameLabel)
rb := serv.ResponseBuilder() rb := serv.ResponseBuilder()
serv.setAccessControlHeaders(rb) serv.setAccessControlHeaders(rb)
rb.SetContentType(gorest.Application_Json) rb.SetContentType(gorest.Application_Json)

View file

@ -14,10 +14,12 @@
package api package api
import ( import (
"github.com/prometheus/prometheus/model"
"github.com/prometheus/prometheus/retrieval"
"net/http" "net/http"
"time" "time"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/retrieval"
) )
type TargetGroup struct { type TargetGroup struct {
@ -37,11 +39,11 @@ func (serv MetricsService) SetTargets(targetGroups []TargetGroup, jobName string
for _, targetGroup := range targetGroups { for _, targetGroup := range targetGroups {
// Do mandatory map type conversion due to Go shortcomings. // Do mandatory map type conversion due to Go shortcomings.
baseLabels := model.LabelSet{ baseLabels := clientmodel.LabelSet{
model.JobLabel: model.LabelValue(job.GetName()), clientmodel.JobLabel: clientmodel.LabelValue(job.GetName()),
} }
for label, value := range targetGroup.BaseLabels { for label, value := range targetGroup.BaseLabels {
baseLabels[model.LabelName(label)] = model.LabelValue(value) baseLabels[clientmodel.LabelName(label)] = clientmodel.LabelValue(value)
} }
for _, endpoint := range targetGroup.Endpoints { for _, endpoint := range targetGroup.Endpoints {