mirror of https://github.com/prometheus/prometheus
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
343 lines
6.6 KiB
343 lines
6.6 KiB
// Copyright 2017 The Prometheus Authors |
|
// 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 tsdb |
|
|
|
import ( |
|
"encoding/binary" |
|
"sort" |
|
"strings" |
|
) |
|
|
|
type memPostings struct { |
|
m map[term][]uint32 |
|
} |
|
|
|
type term struct { |
|
name, value string |
|
} |
|
|
|
// Postings returns an iterator over the postings list for s. |
|
func (p *memPostings) get(t term) Postings { |
|
l := p.m[t] |
|
if l == nil { |
|
return emptyPostings |
|
} |
|
return newListPostings(l) |
|
} |
|
|
|
// add adds a document to the index. The caller has to ensure that no |
|
// term argument appears twice. |
|
func (p *memPostings) add(id uint32, terms ...term) { |
|
for _, t := range terms { |
|
p.m[t] = append(p.m[t], id) |
|
} |
|
} |
|
|
|
// Postings provides iterative access over a postings list. |
|
type Postings interface { |
|
// Next advances the iterator and returns true if another value was found. |
|
Next() bool |
|
|
|
// Seek advances the iterator to value v or greater and returns |
|
// true if a value was found. |
|
Seek(v uint32) bool |
|
|
|
// At returns the value at the current iterator position. |
|
At() uint32 |
|
|
|
// Err returns the last error of the iterator. |
|
Err() error |
|
} |
|
|
|
// errPostings is an empty iterator that always errors. |
|
type errPostings struct { |
|
err error |
|
} |
|
|
|
func (e errPostings) Next() bool { return false } |
|
func (e errPostings) Seek(uint32) bool { return false } |
|
func (e errPostings) At() uint32 { return 0 } |
|
func (e errPostings) Err() error { return e.err } |
|
|
|
var emptyPostings = errPostings{} |
|
|
|
// Intersect returns a new postings list over the intersection of the |
|
// input postings. |
|
func Intersect(its ...Postings) Postings { |
|
if len(its) == 0 { |
|
return emptyPostings |
|
} |
|
if len(its) == 1 { |
|
return its[0] |
|
} |
|
l := len(its) / 2 |
|
return newIntersectPostings(Intersect(its[:l]...), Intersect(its[l:]...)) |
|
} |
|
|
|
type intersectPostings struct { |
|
a, b Postings |
|
aok, bok bool |
|
cur uint32 |
|
} |
|
|
|
func newIntersectPostings(a, b Postings) *intersectPostings { |
|
return &intersectPostings{a: a, b: b} |
|
} |
|
|
|
func (it *intersectPostings) At() uint32 { |
|
return it.cur |
|
} |
|
|
|
func (it *intersectPostings) doNext(id uint32) bool { |
|
for { |
|
if !it.b.Seek(id) { |
|
return false |
|
} |
|
if vb := it.b.At(); vb != id { |
|
if !it.a.Seek(vb) { |
|
return false |
|
} |
|
id = it.a.At() |
|
if vb != id { |
|
continue |
|
} |
|
} |
|
it.cur = id |
|
return true |
|
} |
|
} |
|
|
|
func (it *intersectPostings) Next() bool { |
|
if !it.a.Next() { |
|
return false |
|
} |
|
return it.doNext(it.a.At()) |
|
} |
|
|
|
func (it *intersectPostings) Seek(id uint32) bool { |
|
if !it.a.Seek(id) { |
|
return false |
|
} |
|
return it.doNext(it.a.At()) |
|
} |
|
|
|
func (it *intersectPostings) Err() error { |
|
if it.a.Err() != nil { |
|
return it.a.Err() |
|
} |
|
return it.b.Err() |
|
} |
|
|
|
// Merge returns a new iterator over the union of the input iterators. |
|
func Merge(its ...Postings) Postings { |
|
if len(its) == 0 { |
|
return nil |
|
} |
|
if len(its) == 1 { |
|
return its[0] |
|
} |
|
l := len(its) / 2 |
|
return newMergedPostings(Merge(its[:l]...), Merge(its[l:]...)) |
|
} |
|
|
|
type mergedPostings struct { |
|
a, b Postings |
|
initialized bool |
|
aok, bok bool |
|
cur uint32 |
|
} |
|
|
|
func newMergedPostings(a, b Postings) *mergedPostings { |
|
return &mergedPostings{a: a, b: b} |
|
} |
|
|
|
func (it *mergedPostings) At() uint32 { |
|
return it.cur |
|
} |
|
|
|
func (it *mergedPostings) Next() bool { |
|
if !it.initialized { |
|
it.aok = it.a.Next() |
|
it.bok = it.b.Next() |
|
it.initialized = true |
|
} |
|
|
|
if !it.aok && !it.bok { |
|
return false |
|
} |
|
|
|
if !it.aok { |
|
it.cur = it.b.At() |
|
it.bok = it.b.Next() |
|
return true |
|
} |
|
if !it.bok { |
|
it.cur = it.a.At() |
|
it.aok = it.a.Next() |
|
return true |
|
} |
|
|
|
acur, bcur := it.a.At(), it.b.At() |
|
|
|
if acur < bcur { |
|
it.cur = acur |
|
it.aok = it.a.Next() |
|
} else if acur > bcur { |
|
it.cur = bcur |
|
it.bok = it.b.Next() |
|
} else { |
|
it.cur = acur |
|
it.aok = it.a.Next() |
|
it.bok = it.b.Next() |
|
} |
|
return true |
|
} |
|
|
|
func (it *mergedPostings) Seek(id uint32) bool { |
|
if it.cur >= id { |
|
return true |
|
} |
|
|
|
it.aok = it.a.Seek(id) |
|
it.bok = it.b.Seek(id) |
|
it.initialized = true |
|
|
|
return it.Next() |
|
} |
|
|
|
func (it *mergedPostings) Err() error { |
|
if it.a.Err() != nil { |
|
return it.a.Err() |
|
} |
|
return it.b.Err() |
|
} |
|
|
|
// listPostings implements the Postings interface over a plain list. |
|
type listPostings struct { |
|
list []uint32 |
|
cur uint32 |
|
} |
|
|
|
func newListPostings(list []uint32) *listPostings { |
|
return &listPostings{list: list} |
|
} |
|
|
|
func (it *listPostings) At() uint32 { |
|
return it.cur |
|
} |
|
|
|
func (it *listPostings) Next() bool { |
|
if len(it.list) > 0 { |
|
it.cur = it.list[0] |
|
it.list = it.list[1:] |
|
return true |
|
} |
|
it.cur = 0 |
|
return false |
|
} |
|
|
|
func (it *listPostings) Seek(x uint32) bool { |
|
// If the current value satisfies, then return. |
|
if it.cur >= x { |
|
return true |
|
} |
|
|
|
// Do binary search between current position and end. |
|
i := sort.Search(len(it.list), func(i int) bool { |
|
return it.list[i] >= x |
|
}) |
|
if i < len(it.list) { |
|
it.cur = it.list[i] |
|
it.list = it.list[i+1:] |
|
return true |
|
} |
|
it.list = nil |
|
return false |
|
} |
|
|
|
func (it *listPostings) Err() error { |
|
return nil |
|
} |
|
|
|
// bigEndianPostings implements the Postings interface over a byte stream of |
|
// big endian numbers. |
|
type bigEndianPostings struct { |
|
list []byte |
|
cur uint32 |
|
} |
|
|
|
func newBigEndianPostings(list []byte) *bigEndianPostings { |
|
return &bigEndianPostings{list: list} |
|
} |
|
|
|
func (it *bigEndianPostings) At() uint32 { |
|
return it.cur |
|
} |
|
|
|
func (it *bigEndianPostings) Next() bool { |
|
if len(it.list) >= 4 { |
|
it.cur = binary.BigEndian.Uint32(it.list) |
|
it.list = it.list[4:] |
|
return true |
|
} |
|
return false |
|
} |
|
|
|
func (it *bigEndianPostings) Seek(x uint32) bool { |
|
if it.cur >= x { |
|
return true |
|
} |
|
|
|
num := len(it.list) / 4 |
|
// Do binary search between current position and end. |
|
i := sort.Search(num, func(i int) bool { |
|
return binary.BigEndian.Uint32(it.list[i*4:]) >= x |
|
}) |
|
if i < num { |
|
j := i * 4 |
|
it.cur = binary.BigEndian.Uint32(it.list[j:]) |
|
it.list = it.list[j+4:] |
|
return true |
|
} |
|
it.list = nil |
|
return false |
|
} |
|
|
|
func (it *bigEndianPostings) Err() error { |
|
return nil |
|
} |
|
|
|
type stringset map[string]struct{} |
|
|
|
func (ss stringset) set(s string) { |
|
ss[s] = struct{}{} |
|
} |
|
|
|
func (ss stringset) has(s string) bool { |
|
_, ok := ss[s] |
|
return ok |
|
} |
|
|
|
func (ss stringset) String() string { |
|
return strings.Join(ss.slice(), ",") |
|
} |
|
|
|
func (ss stringset) slice() []string { |
|
slice := make([]string, 0, len(ss)) |
|
for k := range ss { |
|
slice = append(slice, k) |
|
} |
|
sort.Strings(slice) |
|
return slice |
|
}
|
|
|