mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
* TSDB: demistify seriesRefs and ChunkRefs The TSDB package contains many types of series and chunk references, all shrouded in uint types. Often the same uint value may actually mean one of different types, in non-obvious ways. This PR aims to clarify the code and help navigating to relevant docs, usage, etc much quicker. Concretely: * Use appropriately named types and document their semantics and relations. * Make multiplexing and demuxing of types explicit (on the boundaries between concrete implementations and generic interfaces). * Casting between different types should be free. None of the changes should have any impact on how the code runs. TODO: Implement BlockSeriesRef where appropriate (for a future PR) Signed-off-by: Dieter Plaetinck <dieter@grafana.com> * feedback Signed-off-by: Dieter Plaetinck <dieter@grafana.com> * agent: demistify seriesRefs and ChunkRefs Signed-off-by: Dieter Plaetinck <dieter@grafana.com>
178 lines
4.5 KiB
Go
178 lines
4.5 KiB
Go
// Copyright 2021 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 agent
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/prometheus/prometheus/pkg/labels"
|
|
"github.com/prometheus/prometheus/tsdb/chunks"
|
|
)
|
|
|
|
// memSeries is a chunkless version of tsdb.memSeries.
|
|
type memSeries struct {
|
|
sync.Mutex
|
|
|
|
ref chunks.HeadSeriesRef
|
|
lset labels.Labels
|
|
lastTs int64
|
|
}
|
|
|
|
// seriesHashmap is a simple hashmap for memSeries by their label set.
|
|
// It is built on top of a regular hashmap and holds a slice of series to
|
|
// resolve hash collisions. Its methods require the hash to be submitted
|
|
// with the label set to avoid re-computing hash throughout the code.
|
|
type seriesHashmap map[uint64][]*memSeries
|
|
|
|
func (m seriesHashmap) Get(hash uint64, lset labels.Labels) *memSeries {
|
|
for _, s := range m[hash] {
|
|
if labels.Equal(s.lset, lset) {
|
|
return s
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m seriesHashmap) Set(hash uint64, s *memSeries) {
|
|
seriesSet := m[hash]
|
|
for i, prev := range seriesSet {
|
|
if labels.Equal(prev.lset, s.lset) {
|
|
seriesSet[i] = s
|
|
return
|
|
}
|
|
}
|
|
m[hash] = append(seriesSet, s)
|
|
}
|
|
|
|
func (m seriesHashmap) Delete(hash uint64, ref chunks.HeadSeriesRef) {
|
|
var rem []*memSeries
|
|
for _, s := range m[hash] {
|
|
if s.ref != ref {
|
|
rem = append(rem, s)
|
|
}
|
|
}
|
|
if len(rem) == 0 {
|
|
delete(m, hash)
|
|
} else {
|
|
m[hash] = rem
|
|
}
|
|
}
|
|
|
|
// stripeSeries locks modulo ranges of IDs and hashes to reduce lock
|
|
// contention. The locks are padded to not be on the same cache line.
|
|
// Filling the padded space with the maps was profiled to be slower -
|
|
// likely due to the additional pointer dereferences.
|
|
type stripeSeries struct {
|
|
size int
|
|
series []map[chunks.HeadSeriesRef]*memSeries
|
|
hashes []seriesHashmap
|
|
locks []stripeLock
|
|
}
|
|
|
|
type stripeLock struct {
|
|
sync.RWMutex
|
|
// Padding to avoid multiple locks being on the same cache line.
|
|
_ [40]byte
|
|
}
|
|
|
|
func newStripeSeries(stripeSize int) *stripeSeries {
|
|
s := &stripeSeries{
|
|
size: stripeSize,
|
|
series: make([]map[chunks.HeadSeriesRef]*memSeries, stripeSize),
|
|
hashes: make([]seriesHashmap, stripeSize),
|
|
locks: make([]stripeLock, stripeSize),
|
|
}
|
|
for i := range s.series {
|
|
s.series[i] = map[chunks.HeadSeriesRef]*memSeries{}
|
|
}
|
|
for i := range s.hashes {
|
|
s.hashes[i] = seriesHashmap{}
|
|
}
|
|
return s
|
|
}
|
|
|
|
// GC garbage collects old series that have not received a sample after mint
|
|
// and will fully delete them.
|
|
func (s *stripeSeries) GC(mint int64) map[chunks.HeadSeriesRef]struct{} {
|
|
deleted := map[chunks.HeadSeriesRef]struct{}{}
|
|
|
|
for hashLock := 0; hashLock < s.size; hashLock++ {
|
|
s.locks[hashLock].Lock()
|
|
|
|
for hash, all := range s.hashes[hashLock] {
|
|
for _, series := range all {
|
|
series.Lock()
|
|
|
|
// Any series that has received a write since mint is still alive.
|
|
if series.lastTs >= mint {
|
|
series.Unlock()
|
|
continue
|
|
}
|
|
|
|
// The series is stale. We need to obtain a second lock for the
|
|
// ref if it's different than the hash lock.
|
|
refLock := int(series.ref) & (s.size - 1)
|
|
if hashLock != refLock {
|
|
s.locks[refLock].Lock()
|
|
}
|
|
|
|
deleted[series.ref] = struct{}{}
|
|
delete(s.series[refLock], series.ref)
|
|
s.hashes[hashLock].Delete(hash, series.ref)
|
|
|
|
if hashLock != refLock {
|
|
s.locks[refLock].Unlock()
|
|
}
|
|
series.Unlock()
|
|
}
|
|
}
|
|
|
|
s.locks[hashLock].Unlock()
|
|
}
|
|
|
|
return deleted
|
|
}
|
|
|
|
func (s *stripeSeries) GetByID(id chunks.HeadSeriesRef) *memSeries {
|
|
refLock := uint64(id) & uint64(s.size-1)
|
|
s.locks[refLock].RLock()
|
|
defer s.locks[refLock].RUnlock()
|
|
return s.series[refLock][id]
|
|
}
|
|
|
|
func (s *stripeSeries) GetByHash(hash uint64, lset labels.Labels) *memSeries {
|
|
hashLock := hash & uint64(s.size-1)
|
|
|
|
s.locks[hashLock].RLock()
|
|
defer s.locks[hashLock].RUnlock()
|
|
return s.hashes[hashLock].Get(hash, lset)
|
|
}
|
|
|
|
func (s *stripeSeries) Set(hash uint64, series *memSeries) {
|
|
var (
|
|
hashLock = hash & uint64(s.size-1)
|
|
refLock = uint64(series.ref) & uint64(s.size-1)
|
|
)
|
|
s.locks[hashLock].Lock()
|
|
defer s.locks[hashLock].Unlock()
|
|
|
|
if hashLock != refLock {
|
|
s.locks[refLock].Lock()
|
|
defer s.locks[refLock].Unlock()
|
|
}
|
|
|
|
s.hashes[hashLock].Set(hash, series)
|
|
s.series[refLock][series.ref] = series
|
|
}
|