prometheus/storage/metric/view.go
2013-03-21 18:08:49 +01:00

195 lines
5.3 KiB
Go

// 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 (
"fmt"
"github.com/prometheus/prometheus/model"
"github.com/ryszard/goskiplist/skiplist"
"sort"
"time"
)
var (
_ = fmt.Sprintf("")
// firstSupertime is the smallest valid supertime that may be seeked to.
firstSupertime = []byte{0, 0, 0, 0, 0, 0, 0, 0}
// lastSupertime is the largest valid supertime that may be seeked to.
lastSupertime = []byte{127, 255, 255, 255, 255, 255, 255, 255}
)
// Represents the summation of all datastore queries that shall be performed to
// extract values. Each operation mutates the state of the builder.
type ViewRequestBuilder interface {
GetMetricAtTime(fingerprint model.Fingerprint, time time.Time)
GetMetricAtInterval(fingerprint model.Fingerprint, from, through time.Time, interval time.Duration)
GetMetricRange(fingerprint model.Fingerprint, from, through time.Time)
ScanJobs() scanJobs
}
// Contains the various unoptimized requests for data.
type viewRequestBuilder struct {
operations map[model.Fingerprint]ops
}
// Furnishes a ViewRequestBuilder for remarking what types of queries to perform.
func NewViewRequestBuilder() viewRequestBuilder {
return viewRequestBuilder{
operations: make(map[model.Fingerprint]ops),
}
}
// Gets for the given Fingerprint either the value at that time if there is an
// match or the one or two values adjacent thereto.
func (v viewRequestBuilder) GetMetricAtTime(fingerprint model.Fingerprint, time time.Time) {
ops := v.operations[fingerprint]
ops = append(ops, &getValuesAtTimeOp{
time: time,
})
v.operations[fingerprint] = ops
}
// 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
// for each point.
func (v viewRequestBuilder) GetMetricAtInterval(fingerprint model.Fingerprint, from, through time.Time, interval time.Duration) {
ops := v.operations[fingerprint]
ops = append(ops, &getValuesAtIntervalOp{
from: from,
through: through,
interval: interval,
})
v.operations[fingerprint] = ops
}
// Gets for the given Fingerprint either the values that occur inclusively from
// From through Through.
func (v viewRequestBuilder) GetMetricRange(fingerprint model.Fingerprint, from, through time.Time) {
ops := v.operations[fingerprint]
ops = append(ops, &getValuesAlongRangeOp{
from: from,
through: through,
})
v.operations[fingerprint] = ops
}
// Emits the optimized scans that will occur in the data store. This
// effectively resets the ViewRequestBuilder back to a pristine state.
func (v viewRequestBuilder) ScanJobs() (j scanJobs) {
for fingerprint, operations := range v.operations {
sort.Sort(startsAtSort{operations})
j = append(j, scanJob{
fingerprint: fingerprint,
operations: optimize(operations),
})
delete(v.operations, fingerprint)
}
sort.Sort(j)
return
}
type view struct {
fingerprintToSeries map[model.Fingerprint]viewStream
}
func (v view) appendSample(fingerprint model.Fingerprint, timestamp time.Time, value model.SampleValue) {
var (
series, ok = v.fingerprintToSeries[fingerprint]
)
if !ok {
series = newViewStream()
v.fingerprintToSeries[fingerprint] = series
}
series.add(timestamp, value)
}
func (v view) Close() {
v.fingerprintToSeries = make(map[model.Fingerprint]viewStream)
}
func (v view) GetValueAtTime(f model.Fingerprint, t time.Time) (s []model.SamplePair) {
series, ok := v.fingerprintToSeries[f]
if !ok {
return
}
iterator := series.values.Seek(skipListTime(t))
if iterator == nil {
// If the iterator is nil, it means we seeked past the end of the series,
// so we seek to the last value instead. Due to the reverse ordering
// defined on skipListTime, this corresponds to the sample with the
// earliest timestamp.
iterator = series.values.SeekToLast()
if iterator == nil {
// The list is empty.
return
}
}
defer iterator.Close()
if iterator.Key() == nil || iterator.Value() == nil {
return
}
s = append(s, model.SamplePair{
Timestamp: time.Time(iterator.Key().(skipListTime)),
Value: iterator.Value().(value).get(),
})
if iterator.Previous() {
s = append(s, model.SamplePair{
Timestamp: time.Time(iterator.Key().(skipListTime)),
Value: iterator.Value().(value).get(),
})
}
return
}
func (v view) GetBoundaryValues(f model.Fingerprint, i model.Interval) (s []model.SamplePair) {
return
}
func (v view) GetRangeValues(f model.Fingerprint, i model.Interval) (s []model.SamplePair) {
return
}
func newView() view {
return view{
fingerprintToSeries: make(map[model.Fingerprint]viewStream),
}
}
type viewStream struct {
values *skiplist.SkipList
}
func (s viewStream) add(timestamp time.Time, value model.SampleValue) {
s.values.Set(skipListTime(timestamp), singletonValue(value))
}
func newViewStream() viewStream {
return viewStream{
values: skiplist.New(),
}
}