prometheus/db.go

235 lines
5.4 KiB
Go
Raw Normal View History

2016-11-15 09:34:25 +00:00
// Package tsdb implements a time series storage for float64 sample data.
package tsdb
import (
2016-12-04 12:16:11 +00:00
"fmt"
"os"
"sort"
2016-11-15 09:34:25 +00:00
"time"
2016-12-04 12:16:11 +00:00
"github.com/cespare/xxhash"
2016-11-15 09:34:25 +00:00
"github.com/prometheus/common/log"
)
// DefaultOptions used for the DB.
var DefaultOptions = &Options{
StalenessDelta: 5 * time.Minute,
}
// Options of the DB storage.
type Options struct {
StalenessDelta time.Duration
}
// DB is a time series storage.
type DB struct {
logger log.Logger
opts *Options
2016-12-04 12:16:11 +00:00
shards []*SeriesShard
2016-11-15 09:34:25 +00:00
}
2016-12-04 12:16:11 +00:00
// TODO(fabxc): make configurable
const (
numSeriesShards = 32
maxChunkSize = 1024
)
2016-11-15 09:34:25 +00:00
// Open or create a new DB.
func Open(path string, l log.Logger, opts *Options) (*DB, error) {
if opts == nil {
opts = DefaultOptions
}
2016-12-04 12:16:11 +00:00
if err := os.MkdirAll(path, 0777); err != nil {
return nil, err
}
2016-11-15 09:34:25 +00:00
c := &DB{
2016-12-04 12:16:11 +00:00
logger: l,
opts: opts,
2016-11-15 09:34:25 +00:00
}
2016-12-04 12:16:11 +00:00
// Initialize vertical shards.
// TODO(fabxc): validate shard number to be power of 2, which is required
// for the bitshift-modulo when finding the right shard.
for i := 0; i < numSeriesShards; i++ {
c.shards = append(c.shards, NewSeriesShard())
}
// TODO(fabxc): run background compaction + GC.
2016-11-15 09:34:25 +00:00
return c, nil
}
2016-12-04 12:16:11 +00:00
// Close the database.
func (db *DB) Close() error {
return fmt.Errorf("not implemented")
}
// Querier returns a new querier over the database.
func (db *DB) Querier(start, end int64) Querier {
return nil
}
// Matcher matches a string.
type Matcher interface {
// Match returns true if the matcher applies to the string value.
Match(v string) bool
}
// Querier provides querying access over time series data of a fixed
// time range.
type Querier interface {
// Iterator returns an interator over the inverted index that
// matches the key label by the constraints of Matcher.
Iterator(key string, m Matcher) Iterator
// Labels resolves a label reference into a set of labels.
Labels(ref LabelRefs) (Labels, error)
// Series returns series provided in the index iterator.
Series(Iterator) []Series
// Close releases the resources of the Querier.
Close() error
// Range returns the timestamp range of the Querier.
Range() (start, end int64)
}
// Series represents a single time series.
type Series interface {
// LabelsRef returns the label set reference
LabelRefs() LabelRefs
// Iterator returns a new iterator of the data of the series.
Iterator() SeriesIterator
}
// SeriesIterator iterates over the data of a time series.
type SeriesIterator interface {
// Seek advances the iterator forward to the given timestamp.
// If there's no value exactly at ts, it advances to the last value
// before ts.
Seek(ts int64) bool
// Values returns the current timestamp/value pair.
Values() (int64, float64)
// Next advances the iterator by one.
Next() bool
// Err returns the current error.
Err() error
}
type LabelRefs struct {
block uint64
offsets []uint32
}
// Label is a key/value pair of strings.
2016-12-02 16:49:05 +00:00
type Label struct {
Name, Value string
2016-11-15 09:34:25 +00:00
}
2016-12-04 12:16:11 +00:00
// Labels is a sorted set of labels. Order has to be guaranteed upon
2016-12-02 16:49:05 +00:00
// instantiation.
2016-12-04 12:16:11 +00:00
type Labels []Label
func (ls Labels) Len() int { return len(ls) }
func (ls Labels) Swap(i, j int) { ls[i], ls[j] = ls[j], ls[i] }
func (ls Labels) Less(i, j int) bool { return ls[i].Name < ls[j].Name }
// Hash returns a hash value for the label set.
func (ls Labels) Hash() uint64 {
b := make([]byte, 0, 512)
for _, v := range ls {
b = append(b, v.Name...)
b = append(b, '\xff')
b = append(b, v.Value...)
b = append(b, '\xff')
}
return xxhash.Sum64(b)
}
// Get returns the value for the label with the given name.
// Returns an empty string if the label doesn't exist.
func (ls Labels) Get(name string) string {
for _, l := range ls {
if l.Name == name {
return l.Value
}
}
return ""
}
// Equals returns whether the two label sets are equal.
func (ls Labels) Equals(o Labels) bool {
if len(ls) != len(o) {
return false
}
for i, l := range ls {
if l.Name != o[i].Name || l.Value != o[i].Value {
return false
}
}
return true
}
2016-11-15 09:34:25 +00:00
2016-12-04 12:16:11 +00:00
// Map returns a string map of the labels.
func (ls Labels) Map() map[string]string {
m := make(map[string]string, len(ls))
for _, l := range ls {
m[l.Name] = l.Value
}
return m
}
2016-11-15 09:34:25 +00:00
2016-12-04 12:16:11 +00:00
// NewLabels returns a sorted Labels from the given labels.
2016-12-02 16:49:05 +00:00
// The caller has to guarantee that all label names are unique.
2016-12-04 12:16:11 +00:00
func NewLabels(ls ...Label) Labels {
set := make(Labels, 0, len(ls))
2016-12-02 16:49:05 +00:00
for _, l := range ls {
set = append(set, l)
2016-11-15 09:34:25 +00:00
}
2016-12-02 16:49:05 +00:00
sort.Sort(set)
2016-11-15 09:34:25 +00:00
2016-12-02 16:49:05 +00:00
return set
2016-11-15 09:34:25 +00:00
}
2016-12-04 12:16:11 +00:00
// LabelsFromMap returns new sorted Labels from the given map.
func LabelsFromMap(m map[string]string) Labels {
l := make([]Label, 0, len(m))
for k, v := range m {
l = append(l, Label{Name: k, Value: v})
}
return NewLabels(l...)
}
// Vector is a set of LabelSet associated with one value each.
// Label sets and values must have equal length.
2016-12-02 16:49:05 +00:00
type Vector struct {
2016-12-04 12:16:11 +00:00
LabelSets []Labels
Values []float64
2016-11-15 09:34:25 +00:00
}
2016-12-04 12:16:11 +00:00
// AppendVector adds values for a list of label sets for the given timestamp
// in milliseconds.
func (db *DB) AppendVector(ts int64, v *Vector) error {
// Sequentially add samples to shards.
for i, ls := range v.LabelSets {
h := ls.Hash()
shard := db.shards[h>>(64-uint(len(db.shards)))]
// TODO(fabxc): benchmark whether grouping into shards and submitting to
// shards in batches is more efficient.
shard.head.mtx.Lock()
if err := shard.head.append(h, ls, ts, v.Values[i]); err != nil {
shard.head.mtx.Unlock()
// TODO(fabxc): handle gracefully and collect multi-error.
return err
}
shard.head.mtx.Unlock()
}
2016-12-02 16:49:05 +00:00
return nil
2016-12-04 12:16:11 +00:00
}