Merge pull request #175 from prometheus/seriesnotfound

Clarify postings index semantics, handle staleness
This commit is contained in:
Fabian Reinartz 2017-10-12 15:22:44 +02:00 committed by GitHub
commit efe9509d04
5 changed files with 28 additions and 21 deletions

View file

@ -332,20 +332,22 @@ func exitWithError(err error) {
os.Exit(1) os.Exit(1)
} }
func printBlocks(blocks []tsdb.DiskBlock) { func printBlocks(blocks []*tsdb.Block) {
tw := tsdb.GetNewTabWriter(os.Stdout) tw := tsdb.GetNewTabWriter(os.Stdout)
defer tw.Flush() defer tw.Flush()
fmt.Fprintln(tw, "BLOCK ULID\tMIN TIME\tMAX TIME\tNUM SAMPLES\tNUM CHUNKS\tNUM SERIES") fmt.Fprintln(tw, "BLOCK ULID\tMIN TIME\tMAX TIME\tNUM SAMPLES\tNUM CHUNKS\tNUM SERIES")
for _, b := range blocks { for _, b := range blocks {
meta := b.Meta()
fmt.Fprintf(tw, fmt.Fprintf(tw,
"%v\t%v\t%v\t%v\t%v\t%v\n", "%v\t%v\t%v\t%v\t%v\t%v\n",
b.Meta().ULID, meta.ULID,
b.Meta().MinTime, meta.MinTime,
b.Meta().MaxTime, meta.MaxTime,
b.Meta().Stats.NumSamples, meta.Stats.NumSamples,
b.Meta().Stats.NumChunks, meta.Stats.NumChunks,
b.Meta().Stats.NumSeries, meta.Stats.NumSeries,
) )
} }
} }

16
head.go
View file

@ -74,6 +74,7 @@ type headMetrics struct {
series prometheus.Gauge series prometheus.Gauge
seriesCreated prometheus.Counter seriesCreated prometheus.Counter
seriesRemoved prometheus.Counter seriesRemoved prometheus.Counter
seriesNotFound prometheus.Counter
chunks prometheus.Gauge chunks prometheus.Gauge
chunksCreated prometheus.Gauge chunksCreated prometheus.Gauge
chunksRemoved prometheus.Gauge chunksRemoved prometheus.Gauge
@ -103,6 +104,10 @@ func newHeadMetrics(h *Head, r prometheus.Registerer) *headMetrics {
Name: "tsdb_head_series_removed_total", Name: "tsdb_head_series_removed_total",
Help: "Total number of series removed in the head", Help: "Total number of series removed in the head",
}) })
m.seriesNotFound = prometheus.NewCounter(prometheus.CounterOpts{
Name: "tsdb_head_series_not_found",
Help: "Total number of requests for series that were not found.",
})
m.chunks = prometheus.NewGauge(prometheus.GaugeOpts{ m.chunks = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "tsdb_head_chunks", Name: "tsdb_head_chunks",
Help: "Total number of chunks in the head block.", Help: "Total number of chunks in the head block.",
@ -149,6 +154,7 @@ func newHeadMetrics(h *Head, r prometheus.Registerer) *headMetrics {
m.series, m.series,
m.seriesCreated, m.seriesCreated,
m.seriesRemoved, m.seriesRemoved,
m.seriesNotFound,
m.minTime, m.minTime,
m.maxTime, m.maxTime,
m.gcDuration, m.gcDuration,
@ -833,24 +839,17 @@ func (h *headIndexReader) SortedPostings(p Postings) Postings {
if err := p.Err(); err != nil { if err := p.Err(); err != nil {
return errPostings{err: errors.Wrap(err, "expand postings")} return errPostings{err: errors.Wrap(err, "expand postings")}
} }
var err error
sort.Slice(ep, func(i, j int) bool { sort.Slice(ep, func(i, j int) bool {
if err != nil {
return false
}
a := h.head.series.getByID(ep[i]) a := h.head.series.getByID(ep[i])
b := h.head.series.getByID(ep[j]) b := h.head.series.getByID(ep[j])
if a == nil || b == nil { if a == nil || b == nil {
err = errors.Errorf("series not found") level.Debug(h.head.logger).Log("msg", "looked up series not found")
return false return false
} }
return labels.Compare(a.lset, b.lset) < 0 return labels.Compare(a.lset, b.lset) < 0
}) })
if err != nil {
return errPostings{err: err}
}
return newListPostings(ep) return newListPostings(ep)
} }
@ -859,6 +858,7 @@ func (h *headIndexReader) Series(ref uint64, lbls *labels.Labels, chks *[]ChunkM
s := h.head.series.getByID(ref) s := h.head.series.getByID(ref)
if s == nil { if s == nil {
h.head.metrics.seriesNotFound.Inc()
return ErrNotFound return ErrNotFound
} }
*lbls = append((*lbls)[:0], s.lset...) *lbls = append((*lbls)[:0], s.lset...)

View file

@ -525,6 +525,8 @@ type IndexReader interface {
// Postings returns the postings list iterator for the label pair. // Postings returns the postings list iterator for the label pair.
// The Postings here contain the offsets to the series inside the index. // The Postings here contain the offsets to the series inside the index.
// Found IDs are not strictly required to point to a valid Series, e.g. during
// background garbage collections.
Postings(name, value string) (Postings, error) Postings(name, value string) (Postings, error)
// SortedPostings returns a postings list that is reordered to be sorted // SortedPostings returns a postings list that is reordered to be sorted
@ -533,6 +535,7 @@ type IndexReader interface {
// Series populates the given labels and chunk metas for the series identified // Series populates the given labels and chunk metas for the series identified
// by the reference. // by the reference.
// Returns ErrNotFound if the ref does not resolve to a known series.
Series(ref uint64, lset *labels.Labels, chks *[]ChunkMeta) error Series(ref uint64, lset *labels.Labels, chks *[]ChunkMeta) error
// LabelIndices returns the label pairs for which indices exist. // LabelIndices returns the label pairs for which indices exist.

View file

@ -450,6 +450,10 @@ Outer:
for s.p.Next() { for s.p.Next() {
ref := s.p.At() ref := s.p.At()
if err := s.index.Series(ref, &lset, &chunks); err != nil { if err := s.index.Series(ref, &lset, &chunks); err != nil {
// Postings may be stale. Skip if no underlying series exists.
if errors.Cause(err) == ErrNotFound {
continue
}
s.err = err s.err = err
return false return false
} }

View file

@ -702,8 +702,7 @@ func TestBaseChunkSeries(t *testing.T) {
ref: 108, ref: 108,
}, },
}, },
postings: []uint64{12, 10, 108}, postings: []uint64{12, 13, 10, 108}, // 13 doesn't exist and should just be skipped over.
expIdxs: []int{0, 1, 3}, expIdxs: []int{0, 1, 3},
}, },
{ {
@ -718,11 +717,10 @@ func TestBaseChunkSeries(t *testing.T) {
{ {
lset: labels.New([]labels.Label{{"b", "c"}}...), lset: labels.New([]labels.Label{{"b", "c"}}...),
chunks: []ChunkMeta{{Ref: 8282}}, chunks: []ChunkMeta{{Ref: 8282}},
ref: 1, ref: 3,
}, },
}, },
postings: []uint64{}, postings: []uint64{},
expIdxs: []int{}, expIdxs: []int{},
}, },
} }