// ValueAtTime implements SeriesIterator. func (it *memorySeriesIterator) ValueAtTime(t clientmodel.Timestamp) metric.Values { // The most common case. We are iterating through a chunk. if it.chunkIt != nil && it.chunkIt.contains(t) { return it.chunkIt.valueAtTime(t) } if len(it.chunks) == 0 { return nil } // Before or exactly on the first sample of the series. it.chunkIt = it.chunkIterator(0) ts := it.chunkIt.timestampAtIndex(0) if !t.After(ts) { // return first value of first chunk return metric.Values{metric.SamplePair{ Timestamp: ts, Value: it.chunkIt.sampleValueAtIndex(0), }} } // After or exactly on the last sample of the series. it.chunkIt = it.chunkIterator(len(it.chunks) - 1) ts = it.chunkIt.lastTimestamp() if !t.Before(ts) { // return last value of last chunk return metric.Values{metric.SamplePair{ Timestamp: ts, Value: it.chunkIt.sampleValueAtIndex(it.chunkIt.length() - 1), }} } // Find last chunk where firstTime() is before or equal to t. l := len(it.chunks) - 1 i := sort.Search(len(it.chunks), func(i int) bool { return !it.chunks[l-i].firstTime().After(t) }) if i == len(it.chunks) { panic("out of bounds") } it.chunkIt = it.chunkIterator(l - i) ts = it.chunkIt.lastTimestamp() if t.After(ts) { // We ended up between two chunks. sp1 := metric.SamplePair{ Timestamp: ts, Value: it.chunkIt.sampleValueAtIndex(it.chunkIt.length() - 1), } it.chunkIt = it.chunkIterator(l - i + 1) return metric.Values{ sp1, metric.SamplePair{ Timestamp: it.chunkIt.timestampAtIndex(0), Value: it.chunkIt.sampleValueAtIndex(0), }, } } return it.chunkIt.valueAtTime(t) }
// MayContain indicates whether the given SampleKey could potentially contain a // value at the provided time. Even if true is emitted, that does not mean a // satisfactory value, in fact, exists. func (s *SampleKey) MayContain(t clientmodel.Timestamp) bool { switch { case t.Before(s.FirstTimestamp): return false case t.After(s.LastTimestamp): return false default: return true } }
// GetValueAtTime implements SeriesIterator. func (it *memorySeriesIterator) GetValueAtTime(t clientmodel.Timestamp) metric.Values { it.lock() defer it.unlock() // The most common case. We are iterating through a chunk. if it.chunkIt != nil && it.chunkIt.contains(t) { return it.chunkIt.getValueAtTime(t) } it.chunkIt = nil if len(it.chunks) == 0 { return nil } // Before or exactly on the first sample of the series. if !t.After(it.chunks[0].firstTime()) { // return first value of first chunk return it.chunks[0].newIterator().getValueAtTime(t) } // After or exactly on the last sample of the series. if !t.Before(it.chunks[len(it.chunks)-1].lastTime()) { // return last value of last chunk return it.chunks[len(it.chunks)-1].newIterator().getValueAtTime(t) } // Find first chunk where lastTime() is after or equal to t. i := sort.Search(len(it.chunks), func(i int) bool { return !it.chunks[i].lastTime().Before(t) }) if i == len(it.chunks) { panic("out of bounds") } if t.Before(it.chunks[i].firstTime()) { // We ended up between two chunks. return metric.Values{ it.chunks[i-1].newIterator().getValueAtTime(t)[0], it.chunks[i].newIterator().getValueAtTime(t)[0], } } // We ended up in the middle of a chunk. We might stay there for a while, // so save it as the current chunk iterator. it.chunkIt = it.chunks[i].newIterator() return it.chunkIt.getValueAtTime(t) }
// contains implements chunkIterator. func (it *deltaEncodedChunkIterator) contains(t clientmodel.Timestamp) bool { return !t.Before(it.baseT) && !t.After(it.timestampAtIndex(it.len-1)) }
func (t *TieredStorage) loadChunkAroundTime( iterator leveldb.Iterator, fingerprint *clientmodel.Fingerprint, ts clientmodel.Timestamp, firstBlock, lastBlock *SampleKey, ) (chunk metric.Values, expired bool) { if fingerprint.Less(firstBlock.Fingerprint) { return nil, false } if lastBlock.Fingerprint.Less(fingerprint) { return nil, true } seekingKey, _ := t.sampleKeys.Get() defer t.sampleKeys.Give(seekingKey) seekingKey.Fingerprint = fingerprint if fingerprint.Equal(firstBlock.Fingerprint) && ts.Before(firstBlock.FirstTimestamp) { seekingKey.FirstTimestamp = firstBlock.FirstTimestamp } else if fingerprint.Equal(lastBlock.Fingerprint) && ts.After(lastBlock.FirstTimestamp) { seekingKey.FirstTimestamp = lastBlock.FirstTimestamp } else { seekingKey.FirstTimestamp = ts } dto, _ := t.dtoSampleKeys.Get() defer t.dtoSampleKeys.Give(dto) seekingKey.Dump(dto) if !iterator.Seek(dto) { return chunk, true } var foundValues metric.Values if err := iterator.Key(dto); err != nil { panic(err) } seekingKey.Load(dto) if seekingKey.Fingerprint.Equal(fingerprint) { // Figure out if we need to rewind by one block. // Imagine the following supertime blocks with time ranges: // // Block 1: ft 1000 - lt 1009 <data> // Block 1: ft 1010 - lt 1019 <data> // // If we are aiming to find time 1005, we would first seek to the block with // supertime 1010, then need to rewind by one block by virtue of LevelDB // iterator seek behavior. // // Only do the rewind if there is another chunk before this one. if !seekingKey.MayContain(ts) { postValues := unmarshalValues(iterator.RawValue(), nil) if !seekingKey.Equal(firstBlock) { if !iterator.Previous() { panic("This should never return false.") } if err := iterator.Key(dto); err != nil { panic(err) } seekingKey.Load(dto) if !seekingKey.Fingerprint.Equal(fingerprint) { return postValues, false } foundValues = unmarshalValues(iterator.RawValue(), nil) foundValues = append(foundValues, postValues...) return foundValues, false } } foundValues = unmarshalValues(iterator.RawValue(), nil) return foundValues, false } if fingerprint.Less(seekingKey.Fingerprint) { if !seekingKey.Equal(firstBlock) { if !iterator.Previous() { panic("This should never return false.") } if err := iterator.Key(dto); err != nil { panic(err) } seekingKey.Load(dto) if !seekingKey.Fingerprint.Equal(fingerprint) { return nil, false } foundValues = unmarshalValues(iterator.RawValue(), nil) return foundValues, false } } panic("illegal state: violated sort invariant") }
func (cd *chunkDesc) contains(t clientmodel.Timestamp) bool { return !t.Before(cd.firstTime()) && !t.After(cd.lastTime()) }
// contains implements chunkIterator. func (it *deltaEncodedChunkIterator) contains(t clientmodel.Timestamp) bool { return !t.Before(it.chunk.firstTime()) && !t.After(it.chunk.lastTime()) }