mirror of
https://github.com/prometheus/prometheus.git
synced 2025-01-12 22:37:27 -08:00
Squash a number of TODOs.
- Staleness delta is no a proper function parameter and not replicated from package ast. - Named type 'chunks' replaced by explicit '[]chunk' to avoid confusion. - For the same reason, replaced 'chunkDescs' by '[]*chunkDescs'. - Verified that math.Modf is not a speed enhancement over conversion (actually 5x slower). - Renamed firstTimeField, lastTimeField into chunkFirstTime and chunkLastTime. - Verified unpin() is sufficiently goroutine-safe. - Decided not to update archivedFingerprintToTimeRange upon series truncation and added a rationale why. Change-Id: I863b8d785e5ad9f71eb63e229845eacf1bed8534
This commit is contained in:
parent
427c8d53a5
commit
096fa0f8b2
|
@ -119,13 +119,13 @@ func prepareInstantQuery(node Node, timestamp clientmodel.Timestamp, storage loc
|
||||||
preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start()
|
preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start()
|
||||||
p := storage.NewPreloader()
|
p := storage.NewPreloader()
|
||||||
for fp, rangeDuration := range analyzer.FullRanges {
|
for fp, rangeDuration := range analyzer.FullRanges {
|
||||||
if err := p.PreloadRange(fp, timestamp.Add(-rangeDuration), timestamp); err != nil {
|
if err := p.PreloadRange(fp, timestamp.Add(-rangeDuration), timestamp, *stalenessDelta); err != nil {
|
||||||
p.Close()
|
p.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for fp := range analyzer.IntervalRanges {
|
for fp := range analyzer.IntervalRanges {
|
||||||
if err := p.PreloadRange(fp, timestamp, timestamp); err != nil {
|
if err := p.PreloadRange(fp, timestamp, timestamp, *stalenessDelta); err != nil {
|
||||||
p.Close()
|
p.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ func prepareRangeQuery(node Node, start clientmodel.Timestamp, end clientmodel.T
|
||||||
preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start()
|
preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start()
|
||||||
p := storage.NewPreloader()
|
p := storage.NewPreloader()
|
||||||
for fp, rangeDuration := range analyzer.FullRanges {
|
for fp, rangeDuration := range analyzer.FullRanges {
|
||||||
if err := p.PreloadRange(fp, start.Add(-rangeDuration), end); err != nil {
|
if err := p.PreloadRange(fp, start.Add(-rangeDuration), end, *stalenessDelta); err != nil {
|
||||||
p.Close()
|
p.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ func prepareRangeQuery(node Node, start clientmodel.Timestamp, end clientmodel.T
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
for fp := range analyzer.IntervalRanges {
|
for fp := range analyzer.IntervalRanges {
|
||||||
if err := p.PreloadRange(fp, start, end); err != nil {
|
if err := p.PreloadRange(fp, start, end, *stalenessDelta); err != nil {
|
||||||
p.Close()
|
p.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,12 +21,6 @@ import (
|
||||||
"github.com/prometheus/prometheus/storage/metric"
|
"github.com/prometheus/prometheus/storage/metric"
|
||||||
)
|
)
|
||||||
|
|
||||||
// chunks is just a chunk slice. No methods are defined for this named type.
|
|
||||||
// TODO: Perhaps we should remove it? It might avoid errors if it's
|
|
||||||
// syntactically clear that we are dealing with a vanilly slice and not some
|
|
||||||
// kind of more complex collection.
|
|
||||||
type chunks []chunk
|
|
||||||
|
|
||||||
// chunk is the interface for all chunks. Chunks are generally not
|
// chunk is the interface for all chunks. Chunks are generally not
|
||||||
// goroutine-safe.
|
// goroutine-safe.
|
||||||
type chunk interface {
|
type chunk interface {
|
||||||
|
@ -36,7 +30,7 @@ type chunk interface {
|
||||||
// any. The first chunk returned might be the same as the original one
|
// any. The first chunk returned might be the same as the original one
|
||||||
// or a newly allocated version. In any case, take the returned chunk as
|
// or a newly allocated version. In any case, take the returned chunk as
|
||||||
// the relevant one and discard the orginal chunk.
|
// the relevant one and discard the orginal chunk.
|
||||||
add(*metric.SamplePair) chunks
|
add(*metric.SamplePair) []chunk
|
||||||
clone() chunk
|
clone() chunk
|
||||||
firstTime() clientmodel.Timestamp
|
firstTime() clientmodel.Timestamp
|
||||||
lastTime() clientmodel.Timestamp
|
lastTime() clientmodel.Timestamp
|
||||||
|
@ -67,11 +61,11 @@ type chunkIterator interface {
|
||||||
contains(clientmodel.Timestamp) bool
|
contains(clientmodel.Timestamp) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func transcodeAndAdd(dst chunk, src chunk, s *metric.SamplePair) chunks {
|
func transcodeAndAdd(dst chunk, src chunk, s *metric.SamplePair) []chunk {
|
||||||
numTranscodes.Inc()
|
numTranscodes.Inc()
|
||||||
|
|
||||||
head := dst
|
head := dst
|
||||||
body := chunks{}
|
body := []chunk{}
|
||||||
for v := range src.values() {
|
for v := range src.values() {
|
||||||
newChunks := head.add(v)
|
newChunks := head.add(v)
|
||||||
body = append(body, newChunks[:len(newChunks)-1]...)
|
body = append(body, newChunks[:len(newChunks)-1]...)
|
||||||
|
|
|
@ -157,7 +157,7 @@ func (c *deltaEncodedChunk) baseValue() clientmodel.SampleValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add implements chunk.
|
// add implements chunk.
|
||||||
func (c *deltaEncodedChunk) add(s *metric.SamplePair) chunks {
|
func (c *deltaEncodedChunk) add(s *metric.SamplePair) []chunk {
|
||||||
if len(c.buf) < deltaHeaderBytes {
|
if len(c.buf) < deltaHeaderBytes {
|
||||||
c.buf = c.buf[:deltaHeaderBytes]
|
c.buf = c.buf[:deltaHeaderBytes]
|
||||||
binary.LittleEndian.PutUint64(c.buf[deltaHeaderBaseTimeOffset:], uint64(s.Timestamp))
|
binary.LittleEndian.PutUint64(c.buf[deltaHeaderBaseTimeOffset:], uint64(s.Timestamp))
|
||||||
|
@ -172,7 +172,7 @@ func (c *deltaEncodedChunk) add(s *metric.SamplePair) chunks {
|
||||||
if remainingBytes < sampleSize {
|
if remainingBytes < sampleSize {
|
||||||
//fmt.Println("overflow")
|
//fmt.Println("overflow")
|
||||||
overflowChunks := c.newFollowupChunk().add(s)
|
overflowChunks := c.newFollowupChunk().add(s)
|
||||||
return chunks{c, overflowChunks[0]}
|
return []chunk{c, overflowChunks[0]}
|
||||||
}
|
}
|
||||||
|
|
||||||
dt := s.Timestamp - c.baseTime()
|
dt := s.Timestamp - c.baseTime()
|
||||||
|
@ -184,7 +184,7 @@ func (c *deltaEncodedChunk) add(s *metric.SamplePair) chunks {
|
||||||
// existing chunk data into new chunk(s).
|
// existing chunk data into new chunk(s).
|
||||||
//
|
//
|
||||||
// int->float.
|
// int->float.
|
||||||
// TODO: compare speed with Math.Modf.
|
// Note: Using math.Modf is slower than the conversion approach below.
|
||||||
if c.isInt() && clientmodel.SampleValue(int64(dv)) != dv {
|
if c.isInt() && clientmodel.SampleValue(int64(dv)) != dv {
|
||||||
//fmt.Println("int->float", len(c.buf), cap(c.buf), dv)
|
//fmt.Println("int->float", len(c.buf), cap(c.buf), dv)
|
||||||
return transcodeAndAdd(newDeltaEncodedChunk(tb, d4, false), c, s)
|
return transcodeAndAdd(newDeltaEncodedChunk(tb, d4, false), c, s)
|
||||||
|
@ -247,7 +247,7 @@ func (c *deltaEncodedChunk) add(s *metric.SamplePair) chunks {
|
||||||
panic("invalid number of bytes for floating point delta")
|
panic("invalid number of bytes for floating point delta")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return chunks{c}
|
return []chunk{c}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *deltaEncodedChunk) sampleSize() int {
|
func (c *deltaEncodedChunk) sampleSize() int {
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
package local
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
clientmodel "github.com/prometheus/client_golang/model"
|
clientmodel "github.com/prometheus/client_golang/model"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
@ -73,7 +74,11 @@ type SeriesIterator interface {
|
||||||
// them until released via Close(). Its methods are generally not
|
// them until released via Close(). Its methods are generally not
|
||||||
// goroutine-safe.
|
// goroutine-safe.
|
||||||
type Preloader interface {
|
type Preloader interface {
|
||||||
PreloadRange(fp clientmodel.Fingerprint, from clientmodel.Timestamp, through clientmodel.Timestamp) error
|
PreloadRange(
|
||||||
|
fp clientmodel.Fingerprint,
|
||||||
|
from clientmodel.Timestamp, through clientmodel.Timestamp,
|
||||||
|
stalenessDelta time.Duration,
|
||||||
|
) error
|
||||||
/*
|
/*
|
||||||
// GetMetricAtTime loads and pins samples around a given time.
|
// GetMetricAtTime loads and pins samples around a given time.
|
||||||
GetMetricAtTime(clientmodel.Fingerprint, clientmodel.Timestamp) error
|
GetMetricAtTime(clientmodel.Fingerprint, clientmodel.Timestamp) error
|
||||||
|
|
|
@ -247,7 +247,7 @@ func (p *persistence) persistChunk(fp clientmodel.Fingerprint, c chunk) error {
|
||||||
// with the earliest time will have index 0, the following ones will have
|
// with the earliest time will have index 0, the following ones will have
|
||||||
// incrementally larger indexes. It is the caller's responsibility to not
|
// incrementally larger indexes. It is the caller's responsibility to not
|
||||||
// persist or drop anything for the same fingerprint concurrently.
|
// persist or drop anything for the same fingerprint concurrently.
|
||||||
func (p *persistence) loadChunks(fp clientmodel.Fingerprint, indexes []int) (chunks, error) {
|
func (p *persistence) loadChunks(fp clientmodel.Fingerprint, indexes []int) ([]chunk, error) {
|
||||||
// TODO: we need to verify at some point that file length is a multiple of
|
// TODO: we need to verify at some point that file length is a multiple of
|
||||||
// the chunk size. When is the best time to do this, and where to remember
|
// the chunk size. When is the best time to do this, and where to remember
|
||||||
// it? Right now, we only do it when loading chunkDescs.
|
// it? Right now, we only do it when loading chunkDescs.
|
||||||
|
@ -257,7 +257,7 @@ func (p *persistence) loadChunks(fp clientmodel.Fingerprint, indexes []int) (chu
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
chunks := make(chunks, 0, len(indexes))
|
chunks := make([]chunk, 0, len(indexes))
|
||||||
defer func() {
|
defer func() {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
|
@ -270,14 +270,12 @@ func (p *persistence) loadChunks(fp clientmodel.Fingerprint, indexes []int) (chu
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// TODO: check seek offset too?
|
|
||||||
|
|
||||||
n, err := f.Read(typeBuf)
|
n, err := f.Read(typeBuf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if n != 1 {
|
if n != 1 {
|
||||||
// Shouldn't happen?
|
|
||||||
panic("read returned != 1 bytes")
|
panic("read returned != 1 bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,7 +293,7 @@ func (p *persistence) loadChunks(fp clientmodel.Fingerprint, indexes []int) (chu
|
||||||
// loadChunkDescs loads chunkDescs for a series up until a given time. It is
|
// loadChunkDescs loads chunkDescs for a series up until a given time. It is
|
||||||
// the caller's responsibility to not persist or drop anything for the same
|
// the caller's responsibility to not persist or drop anything for the same
|
||||||
// fingerprint concurrently.
|
// fingerprint concurrently.
|
||||||
func (p *persistence) loadChunkDescs(fp clientmodel.Fingerprint, beforeTime clientmodel.Timestamp) (chunkDescs, error) {
|
func (p *persistence) loadChunkDescs(fp clientmodel.Fingerprint, beforeTime clientmodel.Timestamp) ([]*chunkDesc, error) {
|
||||||
f, err := p.openChunkFileForReading(fp)
|
f, err := p.openChunkFileForReading(fp)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -323,7 +321,7 @@ func (p *persistence) loadChunkDescs(fp clientmodel.Fingerprint, beforeTime clie
|
||||||
}
|
}
|
||||||
|
|
||||||
numChunks := int(fi.Size()) / totalChunkLen
|
numChunks := int(fi.Size()) / totalChunkLen
|
||||||
cds := make(chunkDescs, 0, numChunks)
|
cds := make([]*chunkDesc, 0, numChunks)
|
||||||
for i := 0; i < numChunks; i++ {
|
for i := 0; i < numChunks; i++ {
|
||||||
_, err := f.Seek(p.offsetForChunkIndex(i)+chunkHeaderFirstTimeOffset, os.SEEK_SET)
|
_, err := f.Seek(p.offsetForChunkIndex(i)+chunkHeaderFirstTimeOffset, os.SEEK_SET)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -336,8 +334,8 @@ func (p *persistence) loadChunkDescs(fp clientmodel.Fingerprint, beforeTime clie
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cd := &chunkDesc{
|
cd := &chunkDesc{
|
||||||
firstTimeField: clientmodel.Timestamp(binary.LittleEndian.Uint64(chunkTimesBuf)),
|
chunkFirstTime: clientmodel.Timestamp(binary.LittleEndian.Uint64(chunkTimesBuf)),
|
||||||
lastTimeField: clientmodel.Timestamp(binary.LittleEndian.Uint64(chunkTimesBuf[8:])),
|
chunkLastTime: clientmodel.Timestamp(binary.LittleEndian.Uint64(chunkTimesBuf[8:])),
|
||||||
}
|
}
|
||||||
if !cd.firstTime().Before(beforeTime) {
|
if !cd.firstTime().Before(beforeTime) {
|
||||||
// From here on, we have chunkDescs in memory already.
|
// From here on, we have chunkDescs in memory already.
|
||||||
|
@ -480,7 +478,7 @@ func (p *persistence) loadSeriesMapAndHeads() (*seriesMap, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
chunkDescs := make(chunkDescs, numChunkDescs)
|
chunkDescs := make([]*chunkDesc, numChunkDescs)
|
||||||
|
|
||||||
for i := int64(0); i < numChunkDescs; i++ {
|
for i := int64(0); i < numChunkDescs; i++ {
|
||||||
if headChunkPersisted || i < numChunkDescs-1 {
|
if headChunkPersisted || i < numChunkDescs-1 {
|
||||||
|
@ -493,8 +491,8 @@ func (p *persistence) loadSeriesMapAndHeads() (*seriesMap, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
chunkDescs[i] = &chunkDesc{
|
chunkDescs[i] = &chunkDesc{
|
||||||
firstTimeField: clientmodel.Timestamp(firstTime),
|
chunkFirstTime: clientmodel.Timestamp(firstTime),
|
||||||
lastTimeField: clientmodel.Timestamp(lastTime),
|
chunkLastTime: clientmodel.Timestamp(lastTime),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Non-persisted head chunk.
|
// Non-persisted head chunk.
|
||||||
|
|
|
@ -38,7 +38,7 @@ func newTestPersistence(t *testing.T) (*persistence, test.Closer) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildTestChunks() map[clientmodel.Fingerprint]chunks {
|
func buildTestChunks() map[clientmodel.Fingerprint][]chunk {
|
||||||
fps := clientmodel.Fingerprints{
|
fps := clientmodel.Fingerprints{
|
||||||
clientmodel.Metric{
|
clientmodel.Metric{
|
||||||
"label": "value1",
|
"label": "value1",
|
||||||
|
@ -50,10 +50,10 @@ func buildTestChunks() map[clientmodel.Fingerprint]chunks {
|
||||||
"label": "value3",
|
"label": "value3",
|
||||||
}.Fingerprint(),
|
}.Fingerprint(),
|
||||||
}
|
}
|
||||||
fpToChunks := map[clientmodel.Fingerprint]chunks{}
|
fpToChunks := map[clientmodel.Fingerprint][]chunk{}
|
||||||
|
|
||||||
for _, fp := range fps {
|
for _, fp := range fps {
|
||||||
fpToChunks[fp] = make(chunks, 0, 10)
|
fpToChunks[fp] = make([]chunk, 0, 10)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
fpToChunks[fp] = append(fpToChunks[fp], newDeltaEncodedChunk(d1, d1, true).add(&metric.SamplePair{
|
fpToChunks[fp] = append(fpToChunks[fp], newDeltaEncodedChunk(d1, d1, true).add(&metric.SamplePair{
|
||||||
Timestamp: clientmodel.Timestamp(i),
|
Timestamp: clientmodel.Timestamp(i),
|
||||||
|
|
|
@ -14,18 +14,24 @@
|
||||||
package local
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
clientmodel "github.com/prometheus/client_golang/model"
|
clientmodel "github.com/prometheus/client_golang/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// memorySeriesPreloader is a Preloader for the memorySeriesStorage.
|
// memorySeriesPreloader is a Preloader for the memorySeriesStorage.
|
||||||
type memorySeriesPreloader struct {
|
type memorySeriesPreloader struct {
|
||||||
storage *memorySeriesStorage
|
storage *memorySeriesStorage
|
||||||
pinnedChunkDescs chunkDescs
|
pinnedChunkDescs []*chunkDesc
|
||||||
}
|
}
|
||||||
|
|
||||||
// PreloadRange implements Preloader.
|
// PreloadRange implements Preloader.
|
||||||
func (p *memorySeriesPreloader) PreloadRange(fp clientmodel.Fingerprint, from clientmodel.Timestamp, through clientmodel.Timestamp) error {
|
func (p *memorySeriesPreloader) PreloadRange(
|
||||||
cds, err := p.storage.preloadChunksForRange(fp, from, through)
|
fp clientmodel.Fingerprint,
|
||||||
|
from clientmodel.Timestamp, through clientmodel.Timestamp,
|
||||||
|
stalenessDelta time.Duration,
|
||||||
|
) error {
|
||||||
|
cds, err := p.storage.preloadChunksForRange(fp, from, through, stalenessDelta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -93,11 +99,11 @@ func (p *memorySeriesPreloader) GetMetricRangeAtInterval(fp clientmodel.Fingerpr
|
||||||
|
|
||||||
// Close implements Preloader.
|
// Close implements Preloader.
|
||||||
func (p *memorySeriesPreloader) Close() {
|
func (p *memorySeriesPreloader) Close() {
|
||||||
|
// TODO: Idea about a primitive but almost free heuristic to not evict
|
||||||
|
// "recently used" chunks: Do not unpin the chunks right here, but hand
|
||||||
|
// over the pinnedChunkDescs to a manager that will delay the unpinning
|
||||||
|
// based on time and memory pressure.
|
||||||
for _, cd := range p.pinnedChunkDescs {
|
for _, cd := range p.pinnedChunkDescs {
|
||||||
// TODO: unpinning may synchronously cause closing of chunks if they have
|
|
||||||
// been marked to be evicted. This could interfere with other parts of the
|
|
||||||
// storage that check whether a chunk is swapped in or not. Is it a good
|
|
||||||
// idea / sufficient to take the storage lock here?
|
|
||||||
cd.unpin()
|
cd.unpin()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,18 +123,16 @@ func (sm *seriesMap) fpIter() <-chan clientmodel.Fingerprint {
|
||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
type chunkDescs []*chunkDesc
|
|
||||||
|
|
||||||
type chunkDesc struct {
|
type chunkDesc struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
chunk chunk
|
chunk chunk
|
||||||
refCount int
|
refCount int
|
||||||
evict bool
|
evict bool
|
||||||
firstTimeField clientmodel.Timestamp // TODO: stupid name, reorganize.
|
chunkFirstTime clientmodel.Timestamp // Used if chunk is evicted.
|
||||||
lastTimeField clientmodel.Timestamp
|
chunkLastTime clientmodel.Timestamp // Used if chunk is evicted.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cd *chunkDesc) add(s *metric.SamplePair) chunks {
|
func (cd *chunkDesc) add(s *metric.SamplePair) []chunk {
|
||||||
cd.Lock()
|
cd.Lock()
|
||||||
defer cd.Unlock()
|
defer cd.Unlock()
|
||||||
|
|
||||||
|
@ -168,7 +166,7 @@ func (cd *chunkDesc) firstTime() clientmodel.Timestamp {
|
||||||
defer cd.Unlock()
|
defer cd.Unlock()
|
||||||
|
|
||||||
if cd.chunk == nil {
|
if cd.chunk == nil {
|
||||||
return cd.firstTimeField
|
return cd.chunkFirstTime
|
||||||
}
|
}
|
||||||
return cd.chunk.firstTime()
|
return cd.chunk.firstTime()
|
||||||
}
|
}
|
||||||
|
@ -178,7 +176,7 @@ func (cd *chunkDesc) lastTime() clientmodel.Timestamp {
|
||||||
defer cd.Unlock()
|
defer cd.Unlock()
|
||||||
|
|
||||||
if cd.chunk == nil {
|
if cd.chunk == nil {
|
||||||
return cd.lastTimeField
|
return cd.chunkLastTime
|
||||||
}
|
}
|
||||||
return cd.chunk.lastTime()
|
return cd.chunk.lastTime()
|
||||||
}
|
}
|
||||||
|
@ -228,15 +226,15 @@ func (cd *chunkDesc) evictOnUnpin() bool {
|
||||||
|
|
||||||
// evictNow is an internal helper method.
|
// evictNow is an internal helper method.
|
||||||
func (cd *chunkDesc) evictNow() {
|
func (cd *chunkDesc) evictNow() {
|
||||||
cd.firstTimeField = cd.chunk.firstTime()
|
cd.chunkFirstTime = cd.chunk.firstTime()
|
||||||
cd.lastTimeField = cd.chunk.lastTime()
|
cd.chunkLastTime = cd.chunk.lastTime()
|
||||||
cd.chunk = nil
|
cd.chunk = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type memorySeries struct {
|
type memorySeries struct {
|
||||||
metric clientmodel.Metric
|
metric clientmodel.Metric
|
||||||
// Sorted by start time, overlapping chunk ranges are forbidden.
|
// Sorted by start time, overlapping chunk ranges are forbidden.
|
||||||
chunkDescs chunkDescs
|
chunkDescs []*chunkDesc
|
||||||
// Whether chunkDescs for chunks on disk are all loaded. If false, some
|
// Whether chunkDescs for chunks on disk are all loaded. If false, some
|
||||||
// (or all) chunkDescs are only on disk. These chunks are all contiguous
|
// (or all) chunkDescs are only on disk. These chunks are all contiguous
|
||||||
// and at the tail end.
|
// and at the tail end.
|
||||||
|
@ -367,9 +365,9 @@ func (s *memorySeries) purgeOlderThan(t clientmodel.Timestamp) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// preloadChunks is an internal helper method.
|
// preloadChunks is an internal helper method.
|
||||||
func (s *memorySeries) preloadChunks(indexes []int, p *persistence) (chunkDescs, error) {
|
func (s *memorySeries) preloadChunks(indexes []int, p *persistence) ([]*chunkDesc, error) {
|
||||||
loadIndexes := []int{}
|
loadIndexes := []int{}
|
||||||
pinnedChunkDescs := make(chunkDescs, 0, len(indexes))
|
pinnedChunkDescs := make([]*chunkDesc, 0, len(indexes))
|
||||||
for _, idx := range indexes {
|
for _, idx := range indexes {
|
||||||
pinnedChunkDescs = append(pinnedChunkDescs, s.chunkDescs[idx])
|
pinnedChunkDescs = append(pinnedChunkDescs, s.chunkDescs[idx])
|
||||||
if s.chunkDescs[idx].isEvicted() {
|
if s.chunkDescs[idx].isEvicted() {
|
||||||
|
@ -436,7 +434,7 @@ func (s *memorySeries) preloadChunksAtTime(t clientmodel.Timestamp, p *persisten
|
||||||
func (s *memorySeries) preloadChunksForRange(
|
func (s *memorySeries) preloadChunksForRange(
|
||||||
from clientmodel.Timestamp, through clientmodel.Timestamp,
|
from clientmodel.Timestamp, through clientmodel.Timestamp,
|
||||||
fp clientmodel.Fingerprint, p *persistence,
|
fp clientmodel.Fingerprint, p *persistence,
|
||||||
) (chunkDescs, error) {
|
) ([]*chunkDesc, error) {
|
||||||
firstChunkDescTime := through
|
firstChunkDescTime := through
|
||||||
if len(s.chunkDescs) > 0 {
|
if len(s.chunkDescs) > 0 {
|
||||||
firstChunkDescTime = s.chunkDescs[0].firstTime()
|
firstChunkDescTime = s.chunkDescs[0].firstTime()
|
||||||
|
@ -477,7 +475,7 @@ func (s *memorySeries) preloadChunksForRange(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *memorySeries) newIterator(lockFunc, unlockFunc func()) SeriesIterator {
|
func (s *memorySeries) newIterator(lockFunc, unlockFunc func()) SeriesIterator {
|
||||||
chunks := make(chunks, 0, len(s.chunkDescs))
|
chunks := make([]chunk, 0, len(s.chunkDescs))
|
||||||
for i, cd := range s.chunkDescs {
|
for i, cd := range s.chunkDescs {
|
||||||
if !cd.isEvicted() {
|
if !cd.isEvicted() {
|
||||||
if i == len(s.chunkDescs)-1 {
|
if i == len(s.chunkDescs)-1 {
|
||||||
|
@ -521,7 +519,7 @@ func (s *memorySeries) lastTime() clientmodel.Timestamp {
|
||||||
type memorySeriesIterator struct {
|
type memorySeriesIterator struct {
|
||||||
lock, unlock func()
|
lock, unlock func()
|
||||||
chunkIt chunkIterator
|
chunkIt chunkIterator
|
||||||
chunks chunks
|
chunks []chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetValueAtTime implements SeriesIterator.
|
// GetValueAtTime implements SeriesIterator.
|
||||||
|
|
|
@ -151,8 +151,11 @@ func (s *memorySeriesStorage) preloadChunksAtTime(fp clientmodel.Fingerprint, ts
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (s *memorySeriesStorage) preloadChunksForRange(fp clientmodel.Fingerprint, from clientmodel.Timestamp, through clientmodel.Timestamp) (chunkDescs, error) {
|
func (s *memorySeriesStorage) preloadChunksForRange(
|
||||||
stalenessDelta := 300 * time.Second // TODO: Turn into parameter.
|
fp clientmodel.Fingerprint,
|
||||||
|
from clientmodel.Timestamp, through clientmodel.Timestamp,
|
||||||
|
stalenessDelta time.Duration,
|
||||||
|
) ([]*chunkDesc, error) {
|
||||||
s.fpLocker.Lock(fp)
|
s.fpLocker.Lock(fp)
|
||||||
defer s.fpLocker.Unlock(fp)
|
defer s.fpLocker.Unlock(fp)
|
||||||
|
|
||||||
|
@ -312,9 +315,6 @@ func (s *memorySeriesStorage) purgePeriodically(stop <-chan bool) {
|
||||||
glog.Info("Interrupted running series purge.")
|
glog.Info("Interrupted running series purge.")
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
// TODO: Decide whether we also want to adjust the timerange index
|
|
||||||
// entries here. Not updating them shouldn't break anything, but will
|
|
||||||
// make some scenarios less efficient.
|
|
||||||
s.purgeSeries(fp, ts)
|
s.purgeSeries(fp, ts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,12 +348,14 @@ func (s *memorySeriesStorage) purgeSeries(fp clientmodel.Fingerprint, beforeTime
|
||||||
|
|
||||||
// If we arrive here, nothing was in memory, so the metric must have
|
// If we arrive here, nothing was in memory, so the metric must have
|
||||||
// been archived. Drop the archived metric if there are no persisted
|
// been archived. Drop the archived metric if there are no persisted
|
||||||
// chunks left.
|
// chunks left. If we do drop the archived metric, we should update the
|
||||||
if !allDropped {
|
// archivedFingerprintToTimeRange index according to the remaining
|
||||||
return
|
// chunks, but it's probably not worth the effort. Queries going beyond
|
||||||
}
|
// the purge cut-off can be truncated in a more direct fashion.
|
||||||
if err := s.persistence.dropArchivedMetric(fp); err != nil {
|
if allDropped {
|
||||||
glog.Errorf("Error dropping archived metric for fingerprint %v: %v", fp, err)
|
if err := s.persistence.dropArchivedMetric(fp); err != nil {
|
||||||
|
glog.Errorf("Error dropping archived metric for fingerprint %v: %v", fp, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue