mirror of https://github.com/prometheus/prometheus
LabelNames() method to get all unique label names (#369)
* LabelNames() method to get all unique label names Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>pull/5805/head
parent
a95323c021
commit
3a08a71d86
13
block.go
13
block.go
|
@ -83,8 +83,12 @@ type IndexReader interface {
|
||||||
Series(ref uint64, lset *labels.Labels, chks *[]chunks.Meta) error
|
Series(ref uint64, lset *labels.Labels, chks *[]chunks.Meta) error
|
||||||
|
|
||||||
// LabelIndices returns a list of string tuples for which a label value index exists.
|
// LabelIndices returns a list of string tuples for which a label value index exists.
|
||||||
|
// NOTE: This is deprecated. Use `LabelNames()` instead.
|
||||||
LabelIndices() ([][]string, error)
|
LabelIndices() ([][]string, error)
|
||||||
|
|
||||||
|
// LabelNames returns all the unique label names present in the index in sorted order.
|
||||||
|
LabelNames() ([]string, error)
|
||||||
|
|
||||||
// Close releases the underlying resources of the reader.
|
// Close releases the underlying resources of the reader.
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
@ -407,6 +411,10 @@ func (r blockIndexReader) LabelIndices() ([][]string, error) {
|
||||||
return ss, errors.Wrapf(err, "block: %s", r.b.Meta().ULID)
|
return ss, errors.Wrapf(err, "block: %s", r.b.Meta().ULID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r blockIndexReader) LabelNames() ([]string, error) {
|
||||||
|
return r.b.LabelNames()
|
||||||
|
}
|
||||||
|
|
||||||
func (r blockIndexReader) Close() error {
|
func (r blockIndexReader) Close() error {
|
||||||
r.b.pendingReaders.Done()
|
r.b.pendingReaders.Done()
|
||||||
return nil
|
return nil
|
||||||
|
@ -564,6 +572,11 @@ func (pb *Block) OverlapsClosedInterval(mint, maxt int64) bool {
|
||||||
return pb.meta.MinTime <= maxt && mint < pb.meta.MaxTime
|
return pb.meta.MinTime <= maxt && mint < pb.meta.MaxTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LabelNames returns all the unique label names present in the Block in sorted order.
|
||||||
|
func (pb *Block) LabelNames() ([]string, error) {
|
||||||
|
return pb.indexr.LabelNames()
|
||||||
|
}
|
||||||
|
|
||||||
func clampInterval(a, b, mint, maxt int64) (int64, int64) {
|
func clampInterval(a, b, mint, maxt int64) (int64, int64) {
|
||||||
if a < mint {
|
if a < mint {
|
||||||
a = mint
|
a = mint
|
||||||
|
|
43
db.go
43
db.go
|
@ -878,6 +878,49 @@ func (db *DB) CleanTombstones() (err error) {
|
||||||
return errors.Wrap(db.reload(), "reload blocks")
|
return errors.Wrap(db.reload(), "reload blocks")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// labelNames returns all the unique label names from the Block Readers.
|
||||||
|
func labelNames(brs ...BlockReader) (map[string]struct{}, error) {
|
||||||
|
labelNamesMap := make(map[string]struct{})
|
||||||
|
for _, br := range brs {
|
||||||
|
ir, err := br.Index()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "get IndexReader")
|
||||||
|
}
|
||||||
|
names, err := ir.LabelNames()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "LabelNames() from IndexReader")
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
labelNamesMap[name] = struct{}{}
|
||||||
|
}
|
||||||
|
if err = ir.Close(); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "close IndexReader")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return labelNamesMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelNames returns all the unique label names present in the DB in sorted order.
|
||||||
|
func (db *DB) LabelNames() ([]string, error) {
|
||||||
|
brs := []BlockReader{db.head}
|
||||||
|
for _, b := range db.Blocks() {
|
||||||
|
brs = append(brs, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
labelNamesMap, err := labelNames(brs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
labelNames := make([]string, 0, len(labelNamesMap))
|
||||||
|
for name := range labelNamesMap {
|
||||||
|
labelNames = append(labelNames, name)
|
||||||
|
}
|
||||||
|
sort.Strings(labelNames)
|
||||||
|
|
||||||
|
return labelNames, nil
|
||||||
|
}
|
||||||
|
|
||||||
func isBlockDir(fi os.FileInfo) bool {
|
func isBlockDir(fi os.FileInfo) bool {
|
||||||
if !fi.IsDir() {
|
if !fi.IsDir() {
|
||||||
return false
|
return false
|
||||||
|
|
99
db_test.go
99
db_test.go
|
@ -781,7 +781,7 @@ func TestTombstoneClean(t *testing.T) {
|
||||||
testutil.Equals(t, smplExp, smplRes)
|
testutil.Equals(t, smplExp, smplRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, b := range db.blocks {
|
for _, b := range db.Blocks() {
|
||||||
testutil.Equals(t, NewMemTombstones(), b.tombstones)
|
testutil.Equals(t, NewMemTombstones(), b.tombstones)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1134,7 +1134,7 @@ func TestChunkAtBlockBoundary(t *testing.T) {
|
||||||
err = db.compact()
|
err = db.compact()
|
||||||
testutil.Ok(t, err)
|
testutil.Ok(t, err)
|
||||||
|
|
||||||
for _, block := range db.blocks {
|
for _, block := range db.Blocks() {
|
||||||
r, err := block.Index()
|
r, err := block.Index()
|
||||||
testutil.Ok(t, err)
|
testutil.Ok(t, err)
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
@ -1303,6 +1303,101 @@ func TestInitializeHeadTimestamp(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDB_LabelNames(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
// Add 'sampleLabels1' -> Test Head -> Compact -> Test Disk ->
|
||||||
|
// -> Add 'sampleLabels2' -> Test Head+Disk
|
||||||
|
|
||||||
|
sampleLabels1 [][2]string // For checking head and disk separately.
|
||||||
|
// To test Head+Disk, sampleLabels2 should have
|
||||||
|
// at least 1 unique label name which is not in sampleLabels1.
|
||||||
|
sampleLabels2 [][2]string // // For checking head and disk together.
|
||||||
|
exp1 []string // after adding sampleLabels1.
|
||||||
|
exp2 []string // after adding sampleLabels1 and sampleLabels2.
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
sampleLabels1: [][2]string{
|
||||||
|
[2]string{"name1", ""},
|
||||||
|
[2]string{"name3", ""},
|
||||||
|
[2]string{"name2", ""},
|
||||||
|
},
|
||||||
|
sampleLabels2: [][2]string{
|
||||||
|
[2]string{"name4", ""},
|
||||||
|
[2]string{"name1", ""},
|
||||||
|
},
|
||||||
|
exp1: []string{"name1", "name2", "name3"},
|
||||||
|
exp2: []string{"name1", "name2", "name3", "name4"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sampleLabels1: [][2]string{
|
||||||
|
[2]string{"name2", ""},
|
||||||
|
[2]string{"name1", ""},
|
||||||
|
[2]string{"name2", ""},
|
||||||
|
},
|
||||||
|
sampleLabels2: [][2]string{
|
||||||
|
[2]string{"name6", ""},
|
||||||
|
[2]string{"name0", ""},
|
||||||
|
},
|
||||||
|
exp1: []string{"name1", "name2"},
|
||||||
|
exp2: []string{"name0", "name1", "name2", "name6"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
blockRange := DefaultOptions.BlockRanges[0]
|
||||||
|
// Appends samples into the database.
|
||||||
|
appendSamples := func(db *DB, mint, maxt int64, sampleLabels [][2]string) {
|
||||||
|
t.Helper()
|
||||||
|
app := db.Appender()
|
||||||
|
for i := mint; i <= maxt; i++ {
|
||||||
|
for _, tuple := range sampleLabels {
|
||||||
|
label := labels.FromStrings(tuple[0], tuple[1])
|
||||||
|
_, err := app.Add(label, i*blockRange, 0)
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := app.Commit()
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
}
|
||||||
|
for _, tst := range tests {
|
||||||
|
db, close := openTestDB(t, nil)
|
||||||
|
defer close()
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
appendSamples(db, 0, 4, tst.sampleLabels1)
|
||||||
|
|
||||||
|
// Testing head.
|
||||||
|
headIndexr, err := db.head.Index()
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
labelNames, err := headIndexr.LabelNames()
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
testutil.Equals(t, tst.exp1, labelNames)
|
||||||
|
testutil.Ok(t, headIndexr.Close())
|
||||||
|
|
||||||
|
// Testing disk.
|
||||||
|
err = db.compact()
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
// All blocks have same label names, hence check them individually.
|
||||||
|
// No need to aggregrate and check.
|
||||||
|
for _, b := range db.Blocks() {
|
||||||
|
blockIndexr, err := b.Index()
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
labelNames, err = blockIndexr.LabelNames()
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
testutil.Equals(t, tst.exp1, labelNames)
|
||||||
|
testutil.Ok(t, blockIndexr.Close())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addings more samples to head with new label names
|
||||||
|
// so that we can test db.LabelNames() (the union).
|
||||||
|
appendSamples(db, 5, 9, tst.sampleLabels2)
|
||||||
|
|
||||||
|
// Testing DB (union).
|
||||||
|
labelNames, err = db.LabelNames()
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
testutil.Equals(t, tst.exp2, labelNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCorrectNumTombstones(t *testing.T) {
|
func TestCorrectNumTombstones(t *testing.T) {
|
||||||
db, close := openTestDB(t, nil)
|
db, close := openTestDB(t, nil)
|
||||||
defer close()
|
defer close()
|
||||||
|
|
17
head.go
17
head.go
|
@ -1026,6 +1026,21 @@ func (h *headIndexReader) LabelValues(names ...string) (index.StringTuples, erro
|
||||||
return index.NewStringTuples(sl, len(names))
|
return index.NewStringTuples(sl, len(names))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LabelNames returns all the unique label names present in the head.
|
||||||
|
func (h *headIndexReader) LabelNames() ([]string, error) {
|
||||||
|
h.head.symMtx.RLock()
|
||||||
|
defer h.head.symMtx.RUnlock()
|
||||||
|
labelNames := make([]string, 0, len(h.head.values))
|
||||||
|
for name := range h.head.values {
|
||||||
|
if name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
labelNames = append(labelNames, name)
|
||||||
|
}
|
||||||
|
sort.Strings(labelNames)
|
||||||
|
return labelNames, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Postings returns the postings list iterator for the label pair.
|
// Postings returns the postings list iterator for the label pair.
|
||||||
func (h *headIndexReader) Postings(name, value string) (index.Postings, error) {
|
func (h *headIndexReader) Postings(name, value string) (index.Postings, error) {
|
||||||
return h.head.postings.Get(name, value), nil
|
return h.head.postings.Get(name, value), nil
|
||||||
|
@ -1087,9 +1102,7 @@ func (h *headIndexReader) Series(ref uint64, lbls *labels.Labels, chks *[]chunks
|
||||||
func (h *headIndexReader) LabelIndices() ([][]string, error) {
|
func (h *headIndexReader) LabelIndices() ([][]string, error) {
|
||||||
h.head.symMtx.RLock()
|
h.head.symMtx.RLock()
|
||||||
defer h.head.symMtx.RUnlock()
|
defer h.head.symMtx.RUnlock()
|
||||||
|
|
||||||
res := [][]string{}
|
res := [][]string{}
|
||||||
|
|
||||||
for s := range h.head.values {
|
for s := range h.head.values {
|
||||||
res = append(res, []string{s})
|
res = append(res, []string{s})
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ const (
|
||||||
|
|
||||||
indexFormatV1 = 1
|
indexFormatV1 = 1
|
||||||
indexFormatV2 = 2
|
indexFormatV2 = 2
|
||||||
|
|
||||||
|
labelNameSeperator = "\xff"
|
||||||
)
|
)
|
||||||
|
|
||||||
type indexWriterSeries struct {
|
type indexWriterSeries struct {
|
||||||
|
@ -850,9 +852,8 @@ func (r *Reader) SymbolTable() map[uint32]string {
|
||||||
|
|
||||||
// LabelValues returns value tuples that exist for the given label name tuples.
|
// LabelValues returns value tuples that exist for the given label name tuples.
|
||||||
func (r *Reader) LabelValues(names ...string) (StringTuples, error) {
|
func (r *Reader) LabelValues(names ...string) (StringTuples, error) {
|
||||||
const sep = "\xff"
|
|
||||||
|
|
||||||
key := strings.Join(names, sep)
|
key := strings.Join(names, labelNameSeperator)
|
||||||
off, ok := r.labels[key]
|
off, ok := r.labels[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
// XXX(fabxc): hot fix. Should return a partial data error and handle cases
|
// XXX(fabxc): hot fix. Should return a partial data error and handle cases
|
||||||
|
@ -882,14 +883,12 @@ type emptyStringTuples struct{}
|
||||||
func (emptyStringTuples) At(i int) ([]string, error) { return nil, nil }
|
func (emptyStringTuples) At(i int) ([]string, error) { return nil, nil }
|
||||||
func (emptyStringTuples) Len() int { return 0 }
|
func (emptyStringTuples) Len() int { return 0 }
|
||||||
|
|
||||||
// LabelIndices returns a for which labels or label tuples value indices exist.
|
// LabelIndices returns a slice of label names for which labels or label tuples value indices exist.
|
||||||
|
// NOTE: This is deprecated. Use `LabelNames()` instead.
|
||||||
func (r *Reader) LabelIndices() ([][]string, error) {
|
func (r *Reader) LabelIndices() ([][]string, error) {
|
||||||
const sep = "\xff"
|
|
||||||
|
|
||||||
res := [][]string{}
|
res := [][]string{}
|
||||||
|
|
||||||
for s := range r.labels {
|
for s := range r.labels {
|
||||||
res = append(res, strings.Split(s, sep))
|
res = append(res, strings.Split(s, labelNameSeperator))
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
@ -935,6 +934,30 @@ func (r *Reader) SortedPostings(p Postings) Postings {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LabelNames returns all the unique label names present in the index.
|
||||||
|
func (r *Reader) LabelNames() ([]string, error) {
|
||||||
|
labelNamesMap := make(map[string]struct{}, len(r.labels))
|
||||||
|
for key := range r.labels {
|
||||||
|
// 'key' contains the label names concatenated with the
|
||||||
|
// delimiter 'labelNameSeperator'.
|
||||||
|
names := strings.Split(key, labelNameSeperator)
|
||||||
|
for _, name := range names {
|
||||||
|
if name == allPostingsKey.Name {
|
||||||
|
// This is not from any metric.
|
||||||
|
// It is basically an empty label name.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
labelNamesMap[name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
labelNames := make([]string, 0, len(labelNamesMap))
|
||||||
|
for name := range labelNamesMap {
|
||||||
|
labelNames = append(labelNames, name)
|
||||||
|
}
|
||||||
|
sort.Strings(labelNames)
|
||||||
|
return labelNames, nil
|
||||||
|
}
|
||||||
|
|
||||||
type stringTuples struct {
|
type stringTuples struct {
|
||||||
length int // tuple length
|
length int // tuple length
|
||||||
entries []string // flattened tuple entries
|
entries []string // flattened tuple entries
|
||||||
|
|
|
@ -140,11 +140,9 @@ func (m mockIndex) Series(ref uint64, lset *labels.Labels, chks *[]chunks.Meta)
|
||||||
|
|
||||||
func (m mockIndex) LabelIndices() ([][]string, error) {
|
func (m mockIndex) LabelIndices() ([][]string, error) {
|
||||||
res := make([][]string, 0, len(m.labelIndex))
|
res := make([][]string, 0, len(m.labelIndex))
|
||||||
|
|
||||||
for k := range m.labelIndex {
|
for k := range m.labelIndex {
|
||||||
res = append(res, []string{k})
|
res = append(res, []string{k})
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1507,10 +1507,17 @@ func (m mockIndex) Series(ref uint64, lset *labels.Labels, chks *[]chunks.Meta)
|
||||||
|
|
||||||
func (m mockIndex) LabelIndices() ([][]string, error) {
|
func (m mockIndex) LabelIndices() ([][]string, error) {
|
||||||
res := make([][]string, 0, len(m.labelIndex))
|
res := make([][]string, 0, len(m.labelIndex))
|
||||||
|
|
||||||
for k := range m.labelIndex {
|
for k := range m.labelIndex {
|
||||||
res = append(res, []string{k})
|
res = append(res, []string{k})
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m mockIndex) LabelNames() ([]string, error) {
|
||||||
|
labelNames := make([]string, 0, len(m.labelIndex))
|
||||||
|
for name := range m.labelIndex {
|
||||||
|
labelNames = append(labelNames, name)
|
||||||
|
}
|
||||||
|
sort.Strings(labelNames)
|
||||||
|
return labelNames, nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue